业务流程建模与标注(Business Process Model and Notation,BPMN) ,描述流程的基本符号,包括这些图元如何组合成一个业务流程图(Business Process Diagram)
Eclispse画出流程,有两个文件bpmn文件和png文件,其中bpmn文件又可以叫做流程定义文件,它需要遵循BPMN语言规范.png:就是一个单纯的图片,没有任何作用.
bpmn文件一个流程的根元素。一个流程就代表一个工作流。
顺序流是连接两个流程节点的连线,代表一个节点的出口。流程执行完一个节点后,会沿着节点的所有外出顺序流继续执行。 就是说,BPMN 2.0默认的行为就是并发的: 两个外出顺序流会创造两个单独的,并发流程分支。
顺序流主要由4个属性组成:
Id: 唯一标示,用来区分不同的顺序流
sourceRef:连线的源头节点ID
targetRef:连线的目标节点ID
name(可选):连线的名称,不涉及业务,主要用于显示。多出口原则要设置。
说明:
1)结束节点没有出口
其他节点有一个或多个出口。如果有一个出口,则代表是一个单线流程;如果有多个出口,则代表是开启并发流程。
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("流程结束");
}
}
}
@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());
}
开始节点
结束节点:加一个执行监听器,修改流程的状态
任务节点
网关节点
监听器节点
开始事件对应节点
结束事件对应节点
接收任务是一个简单任务,它会等待对应消息的到达。 当前,官方只实现了这个任务的java语义。 当流程达到接收任务,流程状态会保存到数据库中。
在任务创建后,意味着流程会进入等待状态, 直到引擎接收了一个特定的消息, 这会触发流程穿过接收任务继续执行。(短跑比赛一样,预备,信号触发跑)
图形标记:
接收任务显示为一个任务(圆角矩形),右上角有一个消息小标记。 消息是白色的(黑色图标表示发送语义):
1)流程图
销售经理统计当前的营业额,然后短信发送给老板
公共代码增加:
/**
* 通过流程实例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代码如下:
上述两个虽然都可以统称为任务节点,但是还是有本质区别:
1.receiveTask主要代表机器自动执行的,userTask代表人工干预的。
2.receiveTask任务产生后会在act_ru_execution表中新增一条记录, 而userTask产生后会在act_ru_execution和act_ru_task(主要记录任 务的发布时间,办理人等信息)中各产生一条记录。
receiveTask任务提交方式使用RuntimeService的signal方法提交, userTask任务提交方式使用TaskService的complete方法提交。