Activiti会签

Activiti 专栏收录该内容
2 篇文章 0 订阅

1.    什么是会签
a)    在流程业务管理中,任务是通常都是由一个人去处理的,而多个人同时处理一个任务,这种任务我们称之为会签任务。
2.    会签的种类
a)    按数量通过:达到一定数量的通过表决后,会签通过。
b)    按比例通过:达到一定比例的通过表决后,会签通过。
c)    一票否决:只要有一个表决时否定的,会签通过。
d)    一票通过:只要有一个表决通过的,会签通过。
3.    Activiti实现会签
a)    Activiti实现会签是基于多实例任务,将节点设置成多实例,主要通过在UserTask节点的属性上配置

4.    多实例相关属性,以eclipse中的可视化图形操作为例

b)    选则一个用户任务在下面的属性中选择Multil instance,可以看到有很多属性
i.    Sequential:执行顺序。必选项,可选值有true、false。用于设置多实例的执行顺序。True:多实例顺序执行,false:多实例并行
ii.    loop cardinality:循环基数。可选项。可以直接填整数,表示会签的人数。
iii.    Collection:集合。可选项。会签人数的集合,通常为list。和loop cardinality二选一
iv.    Element variable:元素变量。选择Collection时必选,为collection集合每次遍历的元素
v.    Completion condition:完成条件。可选。Activiti会签有个特性,比如设置一个人完成后会签结束,那么其他人的代办任务都会消失。
1.    这里需要介绍一下会签环节中设计的几个默认流程变量
2.    nrOfInstances(numberOfInstances):会签中总共的实例数
3.    nrOfCompletedInstances:已经完成的实例数量
4.    nrOfActiviteInstances:当前活动的实例数量,即还没有完成的实例数量
条件${nrOfInstances == nrOfCompletedInstances}表示所有人员审批完成后会签结束。
条件${ nrOfCompletedInstances == 1}表示一个人完成审批,该会签就结束。
其他条件依次类推,同时这里也可以写自己添加的流程变量。后面实例中会体现

5.任务监听
    a).为了更好的实现会签可以结合监听功能处理,非必选项
b).监听种类:有Java class、Expression、Delegate expression、Alfresco execution script、Alfresco task script,因为监听非必选,所以只介绍Java class类型的监听
    c).监听的触发条件:对于任务监听而言有四种触发条件,
        i.create:任务创建的时候触发监听
        ii.Assignment:设置受理人的时候触发监听
        iii.Complete:任务完成的时候触发监听
        iv.All:以上三种事件都会触发监听
d).监听实现:监听的类需要实现TaskListener接口,重写notify方法
e).添加监听,如下图

如上图所示根据红框中的标记可以很容易的将自己的监听添加到任务中
6.会签实例:以请假申请为例展示会签,会签一人拒绝申请不通过,所有人同意申请通过。
    a).请假流程图以及相属性


这里的Assigner和下图中的Element variable相同

上图中的${signList}是存储会签人员的流程变量,signer为每次遍历时的临时变量名和上图${signer}对应,${pass == false}上文中提到此处可以用会签中默认的流程变量控制会签的过程也可以使用自己定义的流程变量,这里的pass就是自己定义的流程变量,在会签人员处理自己任务是添加到流程中

b).添加监听
i.会签人员完成任务后,需要统计该任务的审批结果,判断最终该会签是通过还是拒绝本实例采用监听的方式处理,新建监听SignListener,
ii.为会签任务添加监听,事件选择complete,如下图

7.完整代码

7.1流程图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/test">
  <process id="applyHoliday2" name="请假申请" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="usertask1" name="申请"></userTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <userTask id="usertask2" name="会签" activiti:assignee="${signer}">
      <extensionElements>
        <activiti:taskListener event="complete" class="cn.com.xu.applyHoliday.SignListener"></activiti:taskListener>
      </extensionElements>
      <multiInstanceLoopCharacteristics isSequential="false" activiti:collection="${signList}" activiti:elementVariable="signer">
        <completionCondition>${pass == false}</completionCondition>
      </multiInstanceLoopCharacteristics>
    </userTask>
    <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
    <userTask id="usertask3" name="记录"></userTask>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow>
    <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
    <sequenceFlow id="flow7" sourceRef="usertask2" targetRef="exclusivegateway1"></sequenceFlow>
    <sequenceFlow id="flow8" name="通过" sourceRef="exclusivegateway1" targetRef="usertask3">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${result == "Y"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow9" name="拒绝" sourceRef="exclusivegateway1" targetRef="usertask1">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${result == "N"}]]></conditionExpression>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_applyHoliday2">
    <bpmndi:BPMNPlane bpmnElement="applyHoliday2" id="BPMNPlane_applyHoliday2">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="250.0" y="210.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
        <omgdc:Bounds height="55.0" width="105.0" x="330.0" y="200.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
        <omgdc:Bounds height="55.0" width="105.0" x="500.0" y="200.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
        <omgdc:Bounds height="55.0" width="105.0" x="860.0" y="200.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="1080.0" y="210.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1">
        <omgdc:Bounds height="40.0" width="40.0" x="710.0" y="207.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="285.0" y="227.0"></omgdi:waypoint>
        <omgdi:waypoint x="330.0" y="227.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="435.0" y="227.0"></omgdi:waypoint>
        <omgdi:waypoint x="500.0" y="227.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
        <omgdi:waypoint x="965.0" y="227.0"></omgdi:waypoint>
        <omgdi:waypoint x="1080.0" y="227.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
        <omgdi:waypoint x="605.0" y="227.0"></omgdi:waypoint>
        <omgdi:waypoint x="710.0" y="227.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">
        <omgdi:waypoint x="750.0" y="227.0"></omgdi:waypoint>
        <omgdi:waypoint x="860.0" y="227.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="14.0" width="100.0" x="750.0" y="227.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
        <omgdi:waypoint x="730.0" y="247.0"></omgdi:waypoint>
        <omgdi:waypoint x="729.0" y="354.0"></omgdi:waypoint>
        <omgdi:waypoint x="382.0" y="354.0"></omgdi:waypoint>
        <omgdi:waypoint x="382.0" y="255.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="14.0" width="100.0" x="730.0" y="247.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

7.2会签监听代码

package cn.com.xu.applyHoliday;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import org.activiti.engine.task.Task;
/**
 * 会签监听
 * @author xuyl 2019年5月7日 上午11:12:42
 *
 */
public class SignListener implements TaskListener{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@Override
	public void notify(DelegateTask delegateTask) {
		System.out.println("会签监听");
		//获取流程id
		String exId = delegateTask.getExecutionId();
		//获取流程参数pass,会签人员完成自己的审批任务时会添加流程参数pass,false为拒绝,true为同意
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		RuntimeService runtimeService = engine.getRuntimeService();
		TaskService taskService = engine.getTaskService();
		boolean pass = (Boolean) runtimeService.getVariable(exId, "pass");
		/*
		 * false:有一个人拒绝,整个流程就结束了,
		 * 	因为Complete condition的值为pass == false,即,当流程参数为pass时会签就结束开始下一个任务
		 * 	所以,当pass == false时,直接设置下一个流程跳转需要的参数
		 * true:审批人同意,同时要判断是不是所有的人都已经完成了,而不是由一个人同意该会签就结束
		 * 	值得注意的是如果一个审批人完成了审批进入到该监听时nrOfCompletedInstances的值还没有更新,因此需要+1
		 */
		if(pass == false){
			//会签结束,设置参数result为N,下个任务为申请
			runtimeService.setVariable(exId, "result", "N");
			//下个任务
			String processInstanceId = delegateTask.getProcessInstanceId();
			Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
			System.out.println("下个任务编码:" + task.getId() + ",下个任务名称:" + task.getName());
		}else{
			Integer complete = (Integer) runtimeService.getVariable(exId, "nrOfCompletedInstances");
			Integer all = (Integer) runtimeService.getVariable(exId, "nrOfInstances");
			//说明都完成了并且没有人拒绝
			if((complete + 1) / all == 1){
				runtimeService.setVariable(exId, "result", "Y");
				//下个任务
				String processInstanceId = delegateTask.getProcessInstanceId();
				Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
				System.out.println("下个任务编码:" + task.getId() + ",下个任务名称:" + task.getName());
			}
		}
	}
	
}

7.3流程图部署以及启动

package cn.com.xu.applyHoliday;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

import cn.com.xu.bean.Emp;

/**
 * 流程部署以及启动
 * @author xuyl 2019年5月7日 上午11:07:36
 *
 */
public class ActivitiDeploy {
	public static void main(String[] args) {
		//获取流程引擎
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		//获取流程存储服务组件
		RepositoryService repositoryService = engine.getRepositoryService();
		//部署流程描述文件
		DeploymentBuilder builder = repositoryService.createDeployment();
		builder.addClasspathResource("bpmn/applyHoliday.bpmn").deploy();
		//获取运行时服务组件
		RuntimeService runtimeService = engine.getRuntimeService();
		TaskService taskService = engine.getTaskService();
		//启动流程引擎
		ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("applyHoliday2");
		//获取身份服务组件
		IdentityService identityService = engine.getIdentityService();
		System.out.println("启动成功,流程id为:" + processInstance.getId());
		//创建模拟用户
		createUser(identityService);
		//启动任务
		startTask(taskService, processInstance.getId());
	}
	/**
	 * 设置会签人员并启动任务
	 * @param taskService
	 * @param proId
	 */
	public static void startTask(TaskService taskService, String proId){
		Task task = taskService.createTaskQuery().processInstanceId(proId).singleResult();
		System.out.println("当前任务编码:" + task.getId() + ",当前任务名称:" + task.getName());
		//设置会签人员
		Map<String, Object> var = new HashMap<String, Object>();
		List<String> signList = new ArrayList<String>();
		signList.add("zhangSan");
		signList.add("liSi");
		var.put("signList", signList);
		taskService.complete(task.getId(), var);
	}
	/**
	 * 新增员工,模拟流程
	 * @param identityService
	 */
	public static void createUser(IdentityService identityService){
		Emp.createUser(identityService, "zhangSan", "张", "三");
		Emp.createUser(identityService, "liSi", "李", "四");
		Emp.createUser(identityService, "wangWu", "王", "五");
		System.out.println("新建员工成功");
	}
}

7.4会签人员任务处理代码

package cn.com.xu.applyHoliday;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;



/**
 * 会签处理
 * @author xuyl 2019年5月7日 上午11:07:12
 *
 */
public class SignHandle {
	public static void main(String[] args) {
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		TaskService taskService = engine.getTaskService();
		handleWaitWork(taskService, "liSi", true);
	}
	/**
	 * 处理代办任务
	 * @param taskService
	 * @param empCode
	 * @param result	审批结果;true:同意;false:拒绝
	 */
	public static void handleWaitWork(TaskService taskService, String empCode, boolean result){
		List<Task> tasks = taskService.createTaskQuery().taskAssignee(empCode).orderByTaskId().desc().list();
		if(tasks != null && tasks.size() != 0){
			for(Task tempTask : tasks){
				//处理代办
				Map<String, Object> map = new HashMap<String, Object>();
				map.put("pass", result);
				taskService.complete(tempTask.getId(), map);
			}
		}else{
			System.out.println("没有代办任务");
		}
	}
}

===================2019/09/03======================

package cn.com.xu.bean;

import java.util.ArrayList;
import java.util.List;

import org.activiti.engine.IdentityService;
import org.activiti.engine.identity.Group;
import org.activiti.engine.identity.User;


/**
 * 创建用户
 * @author C1700092 2019年2月18日 下午3:32:15
 *
 */
public class Emp {
	/**
	 * 创建用户信息
	 * @param service	身份组件
	 * @param empCode	员工编码
	 * @return 
	 */
	public static User createUser(IdentityService service, String empCode, String firstName, String lastName){
		User user = service.newUser(empCode);
		user.setFirstName(firstName);//姓
		user.setLastName(lastName);//名
		user.setPassword("123456");//密码
		service.saveUser(user);
		return service.createUserQuery().userId(empCode).singleResult();
	}
	/**
	 * 根据用户编码查询用户信息
	 * @param service
	 * @param empCode
	 * @return
	 */
	public static User getUserByUserCode(IdentityService service, String empCode){
		return service.createUserQuery().userLastName(empCode).singleResult();
	}
	/**
	 * 根据用户编码查询所属角色
	 * @param service
	 * @param empCode
	 * @return
	 */
	public static List<String> getGroupList(IdentityService service, String empCode){
		List<String> rstList = new ArrayList<String>();
		List<Group> groups = service.createGroupQuery().groupMember(getUserByUserCode(service, empCode).getId()).list();
		if(groups != null && groups.size() != 0){
			for(Group g : groups){
				rstList.add(g.getType());
			}
		}
		return rstList;
	}
}

 

©️2021 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值