【Activiti】从入门到放弃——流程定义语言(BPMN)

什么是BPMN
业务流程建模与标注(Business Process Model and Notation,BPMN) ,描述流程的基本符号,包括这些图元如何组合成一个业务流程图(Business Process Diagram)
Eclispse画出流程,有两个文件bpmn文件和png文件,其中bpmn文件又可以叫做流程定义文件,它需要遵循BPMN语言规范.png:就是一个单纯的图片,没有任何作用.

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

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

2.分支流程-流程图
在这里插入图片描述

3.公共代码抽取

package cn.itsource.activiti.day02;

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.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

public class BaseBpmn {
	private ProcessEngine processEngine=ProcessEngines.getDefaultProcessEngine();
	//自己类和子类都可能使用:使用protected修饰
	protected RepositoryService repositoryService = processEngine.getRepositoryService();
	protected RuntimeService runtimeService = processEngine.getRuntimeService();
	protected TaskService taskService = processEngine.getTaskService();
	
	/**
	 * 
	 * @param name 部署流程的名字
	 * @param resourceName  加载资源的名字前缀
	 * @return
	 */
	protected Deployment deploy(String name, String resourceName) {
		//创建核心
		//获取服务
		//做事情
		// this.getClass().getClassLoader().getResourceAsStream("LeaveFlow.bpmn");从classpath下面加载
		// this.getClass().getResourceAsStream("/LeaveFlow.bpmn");//从classpath下面加载
		// this.getClass().getResourceAsStream("LeaveFlow.bpmn");//从当前类当前包加载(采纳)
		// this.getClass().getResourceAsStream("./LeaveFlow.bpmn");//从当前类当前包加载(采纳)
		String resourceNameBpmn=resourceName+".bpmn";
		String resourceNamePng=resourceName+".png";
		DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
		deploymentBuilder.name(name)
						.addInputStream(resourceNameBpmn, this.getClass().getResourceAsStream(resourceNameBpmn))
						.addInputStream(resourceNamePng, this.getClass().getResourceAsStream(resourceNamePng));
		Deployment deployment = deploymentBuilder.deploy();
		return deployment;
	}


	/**
	 * 
	 * @param processDefinitionKey  启动流程的定义的key
	 */
	protected ProcessInstance startProcess(String processDefinitionKey) {
		
		return runtimeService.startProcessInstanceByKey(processDefinitionKey);
	}
	
	/**
	 * 在一个流程实例中,一个办理人只有一个唯一的任务
	 * @param processInstanceId 流程实例id
	 * @param assignee  办理人
	 * @return
	 */
	protected Task queryPersonalTask(String processInstanceId, String assignee) {
	return 	taskService.createTaskQuery()
		.processInstanceId(processInstanceId)
		.taskAssignee(assignee)
		.singleResult();
	}
}

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());
	}

节点
1)流程图 :销售经理统计当前的营业额,然后短信发送给老板
在这里插入图片描述
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<String, Object> 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("流程已结束!");
		}
	}

网关节点
简单理解有多个分支,但是只能走一个分支。
在这里插入图片描述
在这里插入图片描述
配置文件

<exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway" default="财务"></exclusiveGateway>
<sequenceFlow id="财务" name="小于1000" sourceRef="exclusivegateway1" targetRef="usertask2">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${price<1000}]]></conditionExpression>
    </sequenceFlow>
    <userTask id="usertask3" name="部门经理审批"></userTask>
    <sequenceFlow id="部门经理" name="大于1000小于10000" sourceRef="exclusivegateway1" targetRef="usertask3">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${price>1000&&price<10000}]]></conditionExpression>
    </sequenceFlow>
    <userTask id="usertask4" name="老板审批"></userTask>
    <sequenceFlow id="老板" name="大于10000" sourceRef="exclusivegateway1" targetRef="usertask4">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${price>10000}]]></conditionExpression>
    </sequenceFlow>

说明:
1.一个排他网关对应一个以上的顺序流
2.由排他网关流出的顺序流都有个conditionExpression元素,在内部维护返回boolean类型的决策结果。
3.决策网关只会返回一条结果。当流程执行到排他网关时,流程引擎会自动检索网关出口,从上到下检索如果发现第一条决策结果为true或者没有设置条件的(默认为成立),则流出。
4.如果没有任何一个出口符合条件则抛出异常。

监听器
执行监听
配置文件:
在这里插入图片描述
实现类代码如下
在这里插入图片描述
执行监听器配置可以放在以下三个地方,如图:
在这里插入图片描述
启动流程测试代码如下:
在这里插入图片描述

任务监听:
配置文件:
在这里插入图片描述
说明:
1.任务监听器支持以下属性:
event(必选):任务监听器会被调用的任务类型。 可能的类型为:
 create:任务创建并设置所有属性后触发。
 assignment:任务分配给一些人时触发。 当流程到达userTask,assignment事件 会在create事件之前发生。 这样的顺序似乎不自然,但是原因很简单:当获得create时间时, 我们想获得任务的所有属性,包括执行人。
 complete:当任务完成,并尚未从运行数据中删除时触发。
class:必须调用的代理类。 这个类必须实现org.activiti.engine.delegate.TaskListener 接口。Java代码如下:
在这里插入图片描述
2.运行测试代码得到结果:
流程结束,日志内容为:[Start start, Receive Task start, Receive Task end, Receive Task take, User Task start, User Task assignment, User Task create, User Task complete, User Task end, End end]
新添加的任务监听包裹在executionListener监听的内部,顺序为:execution Start–> task Assignment–>task Create–>task Complete–>execution End–>execution take。

猜你喜欢

转载自blog.csdn.net/qq_43097451/article/details/84638507
今日推荐