Activiti流程定义语言(BPMN)

3.1什么是BPMN

业务流程建模与标注(Business Process Model and Notation,BPMN) ,描述流程的基本符号,包括这些图元如何组合成一个业务流程图(Business Process Diagram)

Eclispse画出流程,有两个文件bpmn文件和png文件,其中bpmn文件又可以叫做流程定义文件,它需要遵循BPMN语言规范.png:就是一个单纯的图片,没有任何作用.

3.2.流程(process)

bpmn文件一个流程的根元素。一个流程就代表一个工作流。

3.3.顺序流(sequenceFlow )

3.3.1.什么是顺序流

顺序流是连接两个流程节点的连线,代表一个节点的出口。流程执行完一个节点后,会沿着节点的所有外出顺序流继续执行。 就是说,BPMN 2.0默认的行为就是并发的: 两个外出顺序流会创造两个单独的,并发流程分支。
顺序流主要由4个属性组成:
Id: 唯一标示,用来区分不同的顺序流
sourceRef:连线的源头节点ID
targetRef:连线的目标节点ID
name(可选):连线的名称,不涉及业务,主要用于显示。多出口原则要设置。
说明:
1)结束节点没有出口
其他节点有一个或多个出口。如果有一个出口,则代表是一个单线流程;如果有多个出口,则代表是开启并发流程。

3.3.2.分支流程-流程图

Activiti流程定义语言(BPMN)_第1张图片
3.3.3.公共代码抽取

package base;

import java.io.InputStream;
import java.util.List;
import java.util.Map;

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.Execution;
import org.activiti.engine.runtime.ExecutionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;

public class BaseBpmn {
	// 获取核心对象 服务大管家
	protected ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
	// 获取运行的service服务
	protected RuntimeService runtimeService = defaultProcessEngine.getRuntimeService();
	// 获取TaskService
	protected TaskService taskService = defaultProcessEngine.getTaskService();
	
	/**
	 * 部署文件
	 * @param resource
	 * @param name
	 */
	public void deploy(String resource,String name){
		// 获取核心对象 服务大管家
		// 获取服务
		RepositoryService repositoryService = defaultProcessEngine.getRepositoryService();
		// 部署对象
		DeploymentBuilder createDeployment = repositoryService.createDeployment();
		// 上传资源
		InputStream inputStream = this.getClass().getResourceAsStream(resource + ".bpmn");
		InputStream inputStream2 = this.getClass().getResourceAsStream(resource + ".png");
		createDeployment.addInputStream(resource + ".bpmn", inputStream);
		createDeployment.addInputStream(resource + ".png", inputStream2);
		createDeployment.name(name);
		// 部署
		createDeployment.deploy();
	}
	/**
	 * 启动实例带流程变量
	 * @param key
	 * @param variables
	 * @return
	 */
	public ProcessInstance start(String key,Map variables){
		//设置变量
		ProcessInstance startProcessInstanceByKey = runtimeService.startProcessInstanceByKey(key,variables);
		return startProcessInstanceByKey;
	}
	
	/**
	 * 启动实例不带流程变量
	 * @param key
	 * @param variables
	 * @return
	 */
	public ProcessInstance start(String key){
		//设置变量
		ProcessInstance startProcessInstanceByKey = runtimeService.startProcessInstanceByKey(key);
		return startProcessInstanceByKey;
	}
	/**
	 * 完成任务,带流程变量,变量流转到下一个节点
	 * @param taskId
	 */
	public void compleTask(String taskId,Map variables){
		taskService.complete(taskId, variables);
	}
	/**
	 * 完成任务,不带带流程变量
	 * @param taskId
	 */
	public void compleTask(String taskId){
		taskService.complete(taskId);
	}
	/**
	 * 完成任务,实例id 和人的名称查询信息
	 * @param id
	 * @param name
	 */
	public void compleTaskByPIdAndName(String id,String name){
		//获取查询对象
		TaskQuery createTaskQuery = taskService.createTaskQuery();
		//获取任务id
		Task task = createTaskQuery.processInstanceId(id).taskAssignee(name).singleResult();
		//完成任务
		taskService.complete(task.getId());
	}
	/**
	 * 完成任务,实例id 和人的名称 带参数查询信息
	 * @param id
	 * @param name
	 */
	public void compleTaskByPIdAndName(String id,String name,Map variables){
		//获取查询对象
		TaskQuery createTaskQuery = taskService.createTaskQuery();
		//获取任务id
		Task task = createTaskQuery.processInstanceId(id).taskAssignee(name).singleResult();
		//完成任务
		taskService.complete(task.getId(),variables);
	}
	/**
	 * 根据名称查询当前任务
	 * @param name
	 * @return
	 */
	public List queryTaskList(String name){
		//获取查询对象
		TaskQuery createTaskQuery = taskService.createTaskQuery();
		//设置查询条件
		createTaskQuery.taskAssignee(name);
		//查询列表
		List list = createTaskQuery.list();
		return list;
	}
	/**
	 * 根据名称查询当前任务
	 * @param name
	 * @return
	 */
	public Task queryTask(String name,String id ){
		//获取查询对象
		TaskQuery createTaskQuery = taskService.createTaskQuery();
		//设置查询条件
		createTaskQuery.taskAssignee(name);
		createTaskQuery.processInstanceId(id);
		//查询列表
		Task task = createTaskQuery.singleResult();
		return task;
	}
	/**
	 * 查询当前指定对象(当前活动节点)
	 * @param id
	 * @param actid
	 * @return
	 */
	public Execution queryExcution(String id,String actid){
		//获取查询对象
		ExecutionQuery createExecutionQuery = runtimeService.createExecutionQuery();
		//设置查询条件
		Execution execution = createExecutionQuery.processInstanceId(id).activityId(actid).singleResult();
		return execution;
	}
	public void isEnd(String id){
		//获取查询对象
		ProcessInstanceQuery createProcessInstanceQuery = runtimeService.createProcessInstanceQuery();
		//根据id查询
		ProcessInstance singleResult = createProcessInstanceQuery.processInstanceId(id).singleResult();
		//判断singleResult
		if(singleResult!=null){
			System.out.println("流程未结束或不存在");
		}else{
			System.out.println("流程结束");
		}
		
	}
	
	
}


3.3.4.分支流程-测试代码 辅助代码:

@Test
	public void  deployTest() throws Exception {
		Deployment deployment = deploy("报销申请","SequesceFlowTest");
		System.out.println("deploymentId:"+deployment.getId());
	}
	
	@Test
	public void  startProcessTest() throws Exception {
			String processDefinitionKey="SequesceFlowTest";
			ProcessInstance processInstance = startProcess(processDefinitionKey);
			System.out.println("ProcessInstanceId:"+processInstance.getId());//2501
	}

测试驳回:

//测试驳回
	/**
	 * ①:先完成报销申请,
	 * ②:走到审批的时候,设置一个flag的流程变量为flase,驳回
	 * ③:回到①,在完成报销申请
	 * ④:审批人又得到审批审批任务
	 * @throws Exception
	 */
	@Test
	public void  notPassTest() throws Exception {
		//①:先完成报销申请,
		String processInstanceId="2501";
		String assignee="小明";
		Task applyTask = queryPersonalTask(processInstanceId,assignee);
		System.out.println("获取申请任务:"+applyTask);
		//先完成报销申请
		taskService.complete(applyTask.getId());
		
		 assignee="小刚";
		Task approveTask = queryPersonalTask(processInstanceId,assignee);
		System.out.println("获取审批任务:"+applyTask);
		// ②:走到审批的时候,设置一个flag的流程变量为flase
		taskService.setVariable(approveTask.getId(),"flag", "false");
		//驳回
		taskService.complete(approveTask.getId());
		
		//④:审批人又得到审批审批任务
		 assignee="小明";
		 applyTask = queryPersonalTask(processInstanceId,assignee);
		System.out.println("再次获取申请任务:"+applyTask);
		
	}

测试通过:

/**
	 * 通过
	 * @throws Exception
	 */
	@Test
	public void  passTest() throws Exception {
		String processInstanceId="2501";
		String assignee="小刚";
		Task approveTask = queryPersonalTask(processInstanceId,assignee);
		System.out.println("获取审批任务:"+approveTask);
		// ②:走到审批的时候,设置一个flag的流程变量为flase
		taskService.setVariable(approveTask.getId(),"flag", "true");
		//通过
		taskService.complete(approveTask.getId());
	}

3.4.节点

开始节点
结束节点:加一个执行监听器,修改流程的状态
任务节点
网关节点
监听器节点

3.4.1.开始事件节点(startEvent)

开始事件对应节点

3.4.2.结束事件节点(endEvent)

结束事件对应节点

3.4.3.任务节点 (Task) 3.4.3.1接收任务节点 (receiveTask)

接收任务是一个简单任务,它会等待对应消息的到达。 当前,官方只实现了这个任务的java语义。 当流程达到接收任务,流程状态会保存到数据库中。
在任务创建后,意味着流程会进入等待状态, 直到引擎接收了一个特定的消息, 这会触发流程穿过接收任务继续执行。(短跑比赛一样,预备,信号触发跑)
图形标记:
接收任务显示为一个任务(圆角矩形),右上角有一个消息小标记。 消息是白色的(黑色图标表示发送语义):
Activiti流程定义语言(BPMN)_第2张图片
1)流程图
销售经理统计当前的营业额,然后短信发送给老板
Activiti流程定义语言(BPMN)_第3张图片

2)测试代码

公共代码增加:

/**
	 * 通过流程实例Id获取流程实例
	 * @param processInstanceId
	 * @return
	 */
	protected ProcessInstance queryProcessInstanceById(String processInstanceId) {
		return runtimeService.createProcessInstanceQuery()
				             .processInstanceId(processInstanceId)
				             .singleResult();
	}
	/**
	 * 在一次流程中通过活动id查询唯一执行对象
	 * 	 * @param pid
	 * @param calcTotalPriceActivityId
	 * @return
	 */
	protected Execution queryExecution(String pid, String calcTotalPriceActivityId) {
		return runtimeService.createExecutionQuery()
				             .processInstanceId(pid)
				             .activityId(calcTotalPriceActivityId)
				             .singleResult();
	}

第一个节点:今日销售额计算

// 第一个节点:计算今日销售额
	@Test
	public void testCalcTotalPrice() throws Exception {
		// 0 查询到"计算今日销售额"的执行对象
		String pid = "2501";
		String calcTotalPriceActivityId = "当天营业额Id";
		Execution calcTotalPriceExecution = queryExecution(pid, calcTotalPriceActivityId);
		System.out.println("获取当天营业额的执行对象!" + calcTotalPriceExecution.getActivityId());
		// 1 计算今日销售额
		double totalPrice = 666666.66666d;
		System.out.println("计算今日销售额为:" + totalPrice);
		// 2 把销售额放入流程变量,共享给下一个节点
		Map processVariables = new HashMap<>();
		processVariables.put("totalPrice", totalPrice);
		// 3 发消息触发"计算今日销售额"执行对象往下走
		System.out.println("设置流程变量,并让它往下走!");
		runtimeService.signal(calcTotalPriceExecution.getId(), processVariables);

		// 4 可以获取下一个节点对应的执行对
		String sendMsgActivityId = "短信发送给老板Id";
		Execution sendMsgExecution = queryExecution(pid, sendMsgActivityId);
		System.out.println("获取到第二个节点:" + sendMsgExecution.getActivityId());
		if (sendMsgExecution != null) {
			System.out.println("第一个节点已经处理完毕!");
		}
	}

第二个节点:短信发送给老板

@Test
	public void testSendMsg() throws Exception {
		String pid = "2501";
		String sendMsgActivityId = "短信发送给老板Id";
		Execution sendMsgExecution = queryExecution(pid, sendMsgActivityId);
		// 1 从流程变量种获取销售额
		String executionId = sendMsgExecution.getId();
		Double totalPrice = runtimeService.getVariable(executionId, "totalPrice", Double.class);
		System.out.println("从流程变量中获取今日销售额:" + totalPrice);
		// 2 把销售额发送给老板
		System.out.println("发送短信给老板:今日收获不错,营业额为" + totalPrice);
		// 3 让流程继续往下走
		runtimeService.signal(executionId);
		// 4 判断流程结束
		ProcessInstance processInstance = queryProcessInstanceById(pid);
		if (processInstance == null) {
			System.out.println("流程已结束!");
		}
	}

3.4.3.2用户任务节点 (userTask)
用户任务用来设置必须由人员完成的工作。 当流程执行到用户任务,会创建一个新任务, 并把这个新任务加入到分配人或群组的任务列表中。
图形标记
用户任务显示成一个普通任务(圆角矩形),左上角有一个小用户图标。
在这里插入图片描述
任务分配
用户任务的办理都需要人工的参与。用户任务可以分为两大类。私有任务(待办任务)和公有任务(可接任务)。
1)私有任务
私有任务即有直接分配给指定用户的任务。只有一个用户可以成为任务 的执行者。 在activiti中,用户叫做执行者。 拥有执行者的用户任务 (即私有任务)对用户是不可见的。

办理者指定分三种方式:
1)写死
2)流程变量
3)监听器
公共代码增加:

	/**
	 * 通过流程定义key启动流程并且设置流程变量
	 * @param processDefinitionKey
	 * @return
	 */
	protected ProcessInstance startProcess(String processDefinitionKey,Map variables) {
		return runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
	}


/**
 * 私有任务测试
 *     办理者指定:
 *        1)写死,设计流程时写死办理者(不用)
 *           缺点:这个流程只能由写死哪个人申请
 *        2)流程变量设置--第一个节点
 *            ①设计流程时要从流程变量中获取办理者
 *            ②流程执行时要设置对应流程变量-启动时设置
 *          问题:如果第二个-n个节点通过在启动流程时设置变量来指定办理者,
 *              第一个办理长时间没有办理,在这期间后面节点办理者离职了,走下去再离职人员手上还会挂任务.
 *             方案1:下一个任务办理时再指定下一个任务办理者.不好,业务逻辑耦合
 *             方案2:使用监听器设置下一个节点启动时指定人,不需要耦合.(采纳)
 *        3)监听器--第二个-n个节点
 *           ①写一个类实现监听器类-TaskListener
 *           ②把写好类绑定到对应节点
 *           ③实现监听器逻辑
 * @author Administrator
 *
 */
public class PersonalTaskTest extends BaseBpmnTest {

	
	//写死
	@Test
	public void test1() {
		
		//部署
		deploy("私有任务测试1", "PersonalTaskTest");
		//启动
		String processDefinitionKey = "PersonalTaskTest";
		ProcessInstance pi = startProcess(processDefinitionKey );
		//获取写死办理的任务
		String assignee = "小鑫鑫";
		Task task = queryPersonalTask(pi.getId(), assignee );
		System.out.println("id:"+task.getId()
		+",name:"+task.getName()+",assignee:"+task.getAssignee());
		
	}
	
	@Test
	public void test2() {
		
		//部署
		//deploy("私有任务测试2", "PersonalTaskTest2");
		//启动时要设置第一个节点的办理者
		String processDefinitionKey = "PersonalTaskTest2";
		
		Map variables = new HashMap<>();
		//第一个节点办理者
		String assignee = "晓鑫鑫";//当前登录用户名称
		variables.put("apply", assignee);
		ProcessInstance pi = startProcess(processDefinitionKey,variables );
	   System.out.println("pid:"+pi.getId());
		
		//获取写死办理的任务
		Task task = queryPersonalTask(pi.getId(), assignee );
		System.out.println("id:"+task.getId()
		+",name:"+task.getName()+",assignee:"+task.getAssignee());
		
	}
	
	//要把第一个节点走下去
	/**
	 * 使用流程变量设置
	 * @throws Exception
	 */
	@Test
	public void test3Pre() throws Exception {
		//部署
		//deploy("私有任务测试3", "PersonalTaskTest3");
		//启动时要设置第一个节点的办理者
		String processDefinitionKey = "PersonalTaskTest3";
		
		Map variables = new HashMap<>();
		//第一个节点办理者
		String assignee = "逗比鑫";//当前登录用户名称
		variables.put("apply", assignee);
		ProcessInstance pi = startProcess(processDefinitionKey,variables );
	   System.out.println("pid:"+pi.getId());
		
		//获取写死办理的任务
		Task task = queryPersonalTask(pi.getId(), assignee );
		System.out.println("id:"+task.getId()
		+",name:"+task.getName()+",assignee:"+task.getAssignee());
		
		taskService.complete(task.getId());
	}
	
	//上一个任务完成设置下一个任务的办理者,没法做,没有办法直接获取下一个任务并设置
	//当前任务创建时,再指定办理者
	@Test
	public void test3() throws Exception {
		String assignee = "逗比鑫"+"审批人";
		String processInstanceId = "5001";
		Task task = queryPersonalTask(processInstanceId, assignee );
		System.out.println("id:"+task.getId()
		+",name:"+task.getName()+",assignee:"+task.getAssignee());
		
		//完成第二个任务
		taskService.complete(task.getId());
		
		//判断流程是否结束
		ProcessInstance pi = queryProcessInstanceById(processInstanceId);
		if (pi == null) {
			System.out.println("流程结束");
		}
	}

}

在下一个节点创建时,增加这个监听器:

import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;

/**
 * ①写一个类实现监听器类-TaskListener
 * ②把写好类绑定到对应节点
 * ③实现监听器逻辑
 * @author Administrator
 *
 */
public class ManagerSettingListener implements TaskListener{

	@Override
	public void notify(DelegateTask delegateTask) {
		System.out.println(delegateTask.getName());
		
		//1 获取申请人
		String apply = delegateTask.getVariable("apply", String.class);
		//2 通过申请人获取审批人  通过申请人获取部门经理
		String manager = apply+"审批人";
		//3 设置办理者
		delegateTask.setAssignee(manager);
		
		//delegateTask.addCandidateUser(userId);
		//delegateTask.addCandidateGroup(groupId);
	}
}

2)公共任务
有的用户任务在指派时无法确定具体的办理者,这时任务也可以加入到人员的候选任务列表中,然后让这些人员选择性认领和办理任务。
公有任务的分配可以分为指定候选用户和候选组两种。
每一种都要三种指定方式
1)写死(不用的)
2)流程变量指定第一个节点
3)任务监听器指定第2-n节点

a)把任务添加到一批用户的候选任务列表中,使用candidateUsers 属 性,XML内容如下:


candidateUsers属性内为用户的ID,多个用户ID之间使用(半角)逗号间隔。

b)把任务添加到一个或多个候选组下,这时任务对组下的所有用户可 见,首先得保证每个组下面有用户,通过IdentityService对象创建用户 和组,然后把用户添加到对应的组下,java代码如下:
Activiti流程定义语言(BPMN)_第4张图片
上述两个虽然都可以统称为任务节点,但是还是有本质区别:
1.receiveTask主要代表机器自动执行的,userTask代表人工干预的。
2.receiveTask任务产生后会在act_ru_execution表中新增一条记录, 而userTask产生后会在act_ru_execution和act_ru_task(主要记录任 务的发布时间,办理人等信息)中各产生一条记录。
receiveTask任务提交方式使用RuntimeService的signal方法提交, userTask任务提交方式使用TaskService的complete方法提交。

你可能感兴趣的:(Activiti工作流)