流程中的任务
1.用户任务
用户任务
:用于定义流程中需要人工参与的任务。
用户任务可以在流程中创建并分配给特定的用户或用户组。当流程执行到用户任务时,流程将暂停,并等待相应的用户完成该任务。完成用户任务后,流程将继续执行。
用户任务可以有以下属性:
- 名称:用户任务的名称,用于标识任务。
- 分配人:用户任务分配给的具体用户或用户组。
- 优先级:用户任务的优先级,用于确定任务的重要程度。
- 截止日期:用户任务的截止日期,用于确定任务的完成期限。
- 表单:用户任务需要填写的表单,用于指导用户完成任务。
在 Activiti7 中,可以使用 BPMN(业务流程建模与标记语言)来定义用户任务。以下是一个使用 Activiti7 定义用户任务的 BPMN 示例:
<process id="userTaskProcess" name="User Task Process" isExecutable="true">
<startEvent id="startEvent" name="Start Event"></startEvent>
<userTask id="userTask" name="User Task" activiti:assignee="user1"></userTask>
<endEvent id="endEvent" name="End Event"></endEvent>
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="userTask"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="userTask" targetRef="endEvent"></sequenceFlow>
</process>
在上述示例中,定义了一个名为 “User Task Process” 的流程,其中包含了一个用户任务 “User Task”,并将其分配给 “user1”。流程从 “Start Event” 开始,经过用户任务后,最终到达 “End Event”。
通过 Activiti7 用户任务,可以有效管理和跟踪流程中需要人工参与的任务,并提高流程的效率和可靠性。
部署流程后启动流程
@SpringBootTest
class Activiti7Demo02ApplicationTests {
@Autowired
private ProcessEngine processEngine;
@Autowired
private TaskService taskService;
private RepositoryService repositoryService;
/**
* 部署流程
*/
@Test
void deployFlow() {
Deployment deploy = processEngine.getRepositoryService().createDeployment()
.addClasspathResource("flow/user-task-demo.bpmn20.xml")
.name("用户任务")
.deploy();
System.out.println(deploy.getId());
}
/**
* 发起流程
*/
@Test
void startFlow(){
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceById("event-compensation:2:340003");
String description = processInstance.getDescription();
System.out.println("description = " + description);
System.out.println("processInstance.getId() = " + processInstance.getId());
}
}
在act_ru_task
可以看到我们定义的过期时间
然后我们也可以通过API
来查询对应的任务信息
/**
* 任务查询
*/
@Test
void queryTask(){
Task task = taskService.createTaskQuery().taskId("342505").singleResult();
System.out.println("task.getDescription() = " + task.getDescription());
System.out.println("task.getDueDate() = " + task.getDueDate());
}
这里需要注意dueDate属性只是标识该用户任务何时过期,但过期后不会自动完成。我们可以利用这个字段做相关的逻辑处理
2. 手动任务
在Activiti 7中,手动任务是一种用户任务,需要人工干预
才能完成的任务。手动任务不需要任何自动化逻辑,只需要一个人工干预的环节。
手动任务
可以用来引导用户参与流程的执行。当流程执行到手动任务时,流程将会暂停,直到有人手动完成该任务。
手动任务可以有多种形式,比如填写表单、审批文件、上传附件等。根据实际需求,可以自定义手动任务的表单和处理逻辑。
在手动任务中我们通过executionListener
来监听任务的执行
public class ManualTaskExecutionListener implements ExecutionListener {
@Override
public void notify(DelegateExecution execution) {
FlowElement currentFlowElement = execution.getCurrentFlowElement();
System.out.println("当前的节点:"+currentFlowElement.getName());
}
}
部署审批任务后手动任务
是直接走过。而且在act_hi_taskinst
中也不会有相关的记录信息
3. 接收任务
接收任务
和手动任务
类似,不同之处在于手动任务
会直接通过,而接收任务
则会停下来等待触发,只有被触发才会继续流转。Activiti7 中的接收任务用于在流程中等待特定的消息或事件的到来,然后继续流程的执行。接收任务可以看作是一个等待状态,直到接收到相关的消息或事件,然后流程会根据接收到的消息或事件继续向下执行。
然后我们正常的部署
->启动
->审批
到了接收任务
这个节点。在act_ru_task
是没有对应的待办任务的。当是在act_ru_execution
中是可以看到相关的执行实例的。
处理完线下的逻辑后我们就可以触发这个节点的执行了
/**
* 触发接收任务
*/
@Test
public void test9() throws Exception{
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = engine.getRuntimeService();
runtimeService.trigger("362502"); // 触发 接收任务
}
当然我们也可以在接收任务
节点绑定相关的监听器来记录相关的信息
public class ReceiveTaskListener implements ExecutionListener {
@Override
public void notify(DelegateExecution execution) {
FlowElement currentFlowElement = execution.getCurrentFlowElement();
System.out.println("当前任务节点:"+currentFlowElement.getName());
System.out.println("接收任务节点已被触发...");
}
}
触发任务后可以看到相关的日志信息
4. 服务任务
服务任务是一种自动执行的活动,无须人工参与,可以通过调用Java代码实现自定义的业务逻辑。
4.1 基本应用
通过案例来介绍
在服务任务中我们可以通过三种方式来设置对应的处理对象。
- 通过activiti:class来指定Java类
- 通过activiti:delegateExpression类使用委托表达式
- 通过activiti:expression来使用UEL表达式
上面的案例中我们就可以使用这三种方式来处理:
然后对应的java类:
public class MyFirstDelegate implements JavaDelegate {
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("服务任务1..." + LocalDateTime.now().toString());
FlowElement currentFlowElement = execution.getCurrentFlowElement();
System.out.println("currentFlowElement = " + currentFlowElement);
}
}
@Component
public class MySecondDelegate implements JavaDelegate {
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("服务任务2..." + LocalDateTime.now().toString());
FlowElement currentFlowElement = execution.getCurrentFlowElement();
System.out.println("currentFlowElement = " + currentFlowElement);
}
}
@Component
public class MyThirdDelegate {
/**
* 处理方法
* @param execution
*/
public void handleFun1(DelegateExecution execution) {
System.out.println("服务任务3..." + LocalDateTime.now().toString());
FlowElement currentFlowElement = execution.getCurrentFlowElement();
System.out.println("currentFlowElement = " + currentFlowElement);
}
}
后面两种情况我们需要把对象注入到Spring容器中。第二种情况需要实现JavaDelegate
接口。然后测试即可。
部署流程后的启动操作可以看到相关的日志信息
当然在通过UEL表达式处理的时候我们是可以声明相关的变量的。
@Component
public class MyThirdDelegate {
/**
* 处理方法
* @param execution
*/
public void handleFun1(DelegateExecution execution,Integer age) {
System.out.println("服务任务3..." + LocalDateTime.now().toString());
FlowElement currentFlowElement = execution.getCurrentFlowElement();
System.out.println("currentFlowElement = " + currentFlowElement);
}
}
比如上面的handleFun1
中既有DelegateExecution
也有自定义的Integer
属性。这时我们在声明的时候就需要同步的声明
<serviceTask id="sid-185E2348-E3FC-437E-8DC1-E71A9750F507"
name="服务任务3" activiti:expression="${myThirdDelegate.handleFun1(execution,age)}">
</serviceTask>
上面的第一个参数是DelegateExecution
在表达式中默认的名称是execution
,第二个参数是当前流程实例中的名称为age
的流程变量。
4.2 属性信息
在针对服务任务
的处理中,有时需要接收相关的属性信息。这块我们介绍下具体应该要如何的实现。首先针对的是需要传递的是常量
信息或者是固定值的方式,比如下面的情况
/**
* 自定义的委托类
*/
public class MyFirstDelegate implements JavaDelegate {
@Setter
private Expression name;
@Setter
private Expression age;
@Setter
private Expression desc;
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("服务任务1..." + LocalDateTime.now().toString());
FlowElement currentFlowElement = execution.getCurrentFlowElement();
System.out.println("currentFlowElement = " + currentFlowElement);
System.out.println("name.getExpressionText() = " + name.getExpressionText());
System.out.println("age.getExpressionText() = " + age.getExpressionText());
System.out.println("desc.getExpressionText() = " + desc.getExpressionText());
}
}
那么我们就可以通过class:field来指定
这块我们在class fields
中通过固定值
的方式来绑定
具体的xml中的定义信息:
<serviceTask id="sid-33AB5184-9C6A-4061-A2BF-CF2E7272AFAF" name="服务任务1" activiti:class="com.boge.activit.delegate.MyFirstDelegate">
<extensionElements>
<activiti:field name="name">
<activiti:string><![CDATA[波哥]]></activiti:string>
</activiti:field>
<activiti:field name="age">
<activiti:string><![CDATA[18]]></activiti:string>
</activiti:field>
<activiti:field name="desc">
<activiti:string><![CDATA[这是一个励志的故事]]></activiti:string>
</activiti:field>
</extensionElements>
</serviceTask>
启动流程后通过日志信息可以看到相关的数据获取到了
上面的例子中通过固定值的方式来处理显然不是太灵活。这时我们同样可以通过UEL
表达式的方式来注入:
对应绑定的JavaDelegate
类为:
public class MyFirstDelegate implements JavaDelegate {
@Setter
private Expression name;
@Setter
private Expression age;
@Setter
private Expression desc;
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("服务任务1..." + LocalDateTime.now().toString());
FlowElement currentFlowElement = execution.getCurrentFlowElement();
System.out.println("currentFlowElement = " + currentFlowElement);
System.out.println("name.getExpressionText() = " + name.getExpressionText());
System.out.println("age.getExpressionText() = " + age.getExpressionText());
System.out.println("desc.getExpressionText() = " + desc.getExpressionText());
System.out.println("name = " + name.getValue(execution));
System.out.println("age = " + age.getValue(execution));
System.out.println("desc = " + desc.getValue(execution));
}
}
然后在绑定class field
中我们设置相关的表达式:
然后我们在部署流程后发起流程实例。在进入该服务任务前我们需要设置相关的流程变量信息
/**
* 发起流程
*/
@Test
void startFlow(){
RuntimeService runtimeService = processEngine.getRuntimeService();
// 启动流程实例的时候就需要绑定相关的流程变量
Map<String,Object> map = new HashMap<>();
map.put("name","boge3306");
map.put("age",66);
map.put("desc","服务任务的动态属性...");
ProcessInstance processInstance = runtimeService
.startProcessInstanceById("demo1:1:3",map);
}
触发JavaDelegate
后可以看到相关的日志信息:
4.3 执行结果
有些情况下我们在服务任务
处理完相关业务后在下一个节点中也需要服务任务执行的返回值,这时可以通过服务执行的返回值处理,具体通过为服务任务定义的activiti:resultVariable属性设置为流程变量,它可以是新的流程变量,也可以是已经存在的流程变量。如果指定为已存在的流程变量,则流程变量的值会被服务执行的结果覆盖。如果不指定返回变量名,则服务任务的结果值将被忽略。
在服务任务中我们通过表达式来定义了${businessStu.getTotalScore()}
,并通过Result variable name
来绑定方法的返回信息
<serviceTask id="sid-CF377576-9674-432F-B950-044EBD097E07" name="服务任务1" activiti:expression="${businessStu.getTotalScore()}" activiti:resultVariableName="totalScore">
然后对应的定义相关的Java类。并注入到Spring容器中。
@Component
public class BusinessStu {
public Integer getTotalScore(){
System.out.println("BusinessStu....执行了");
return 999;
}
}
然后在第二个服务任务中绑定JavaDelegate
。并获取返回的信息
public class MySecondDelegate implements JavaDelegate {
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("服务任务1..." + LocalDateTime.now().toString());
FlowElement currentFlowElement = execution.getCurrentFlowElement();
System.out.println("currentFlowElement = " + currentFlowElement);
Object totalScore = execution.getVariable("totalScore");
System.out.println("totalScore = " + totalScore);
}
}
流程执行后的日志输出
4.4 异常处理
使用服务任务,当执行自定义逻辑时,经常需要捕获对应的业务异常,并在流程中进行处理。对于该问题,Activiti提供了多种解决方式。
可以在服务任务或脚本任务的用户代码中抛出BPMN错误。可以在Java委托、脚本、表达式与委托表达式中,抛出特殊的FlowableException:BpmnError。引擎会捕获这个异常,并将其转发至合适的错误处理器,如错误边界事件或错误事件子流程。
public class MySecondDelegate implements JavaDelegate {
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
try {
System.out.println("服务任务1..." + LocalDateTime.now().toString());
FlowElement currentFlowElement = execution.getCurrentFlowElement();
System.out.println("currentFlowElement = " + currentFlowElement);
Object totalScore = execution.getVariable("totalScore");
System.out.println("totalScore = " + totalScore);
} catch (Exception e) {
throw new BpmnError("BusinessExceptionOccurred");
}
}
}
在这个JavaDelegate类的execute()方法中,执行自定义业务逻辑发生异常时,抛出了BpmnError(加粗部分的代码),该构造函数的参数是业务错误代码,用于决定由哪个错误处理器来响应这个错误。
需要注意的是,这种方式只适用于业务错误,需要通过流程中定义的错误边界事件或错误事件子流程进行处理。而技术上的错误应该使用其他异常类型,通常不在流程内部处理。
4.5 JavaDelegate中使用Activiti服务
有的时候,需要在Java服务任务中使用Activiti7服务(例如调用活动(call activity)不满足需要的场景下,使用RuntimeService启动流程实例)。
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
RuntimeService runtimeService = Context.getProcessEngineConfiguration().getRuntimeService();
runtimeService.startProcessInstanceById("myProcess");
}
在以上代码中,JavaDelegate类的execute()方法通过调用Context.getProcessEngineConfiguration()获取了RuntimeService服务,然后调用了其startProcessInstanceByKey(String processDefinitionKey)方法。可以通过这种方式访问所有Activiti服务的API。
5.脚本任务
脚本任务(script task)是自动执行的活动。当流程执行到达脚本任务时,会执行相应的脚本。Activiti7 脚本任务的作用是允许开发者在工作流程中嵌入脚本语言的代码,以执行特定的任务。脚本任务可以使用多种脚本语言,如JavaScript、Groovy、Python等,根据具体需求选择合适的脚本语言。
脚本任务的主要作用包括:
-
执行复杂的业务逻辑:脚本任务可以执行复杂的计算、条件判断和数据处理等操作,以满足特定的业务需求。
-
数据转换和处理:脚本任务可以对输入的数据进行转换、处理和验证,以确保数据的有效性和一致性。
-
接口调用和集成:脚本任务可以调用外部系统的接口,实现与外部系统的集成和交互。
-
自定义行为和规则:脚本任务可以根据特定的业务规则和条件,执行自定义的行为和操作。
-
动态决策和流程控制:脚本任务可以根据动态的条件和数据,实现流程的动态决策和控制,以适应不同的业务场景。
总之,脚本任务是Activiti7中非常有用的工具,可以在工作流程中执行复杂的业务逻辑和操作,以实现灵活性和可扩展性。
在使用脚本任务时需要指定Script format
和Script
,其中,scriptFormat属性表示脚本格式,其值必须兼容JSR-223(Java平台的脚本语言)。Activiti支持三种脚本任务类型:javascript、groovy和juel。使用groovy需要单独的添加相关的依赖
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.16</version>
</dependency>
脚本的具体代码为
然后在启动流程中我们需要设置inputArray
的这个流程变量。注意这个是一个Integer的数组哦
/**
* 任务审批
*/
@Test
void completeTask(){
Map<String,Object> map = new HashMap<>();
map.put("inputArray",new Integer[]{1,2,3,4,5,6,7,8,9});
taskService.complete("32505",map);
}
然后执行任务后可以看到相关的日志信息
6.邮件服务
6.1 开启邮箱设置
以QQ邮箱为例我们需要设置开启相关的服务。设置
–》账户
然后往下面找到POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务
开启服务。并且记住相关的授权码信息:
这块我们需要记住对应的授权码信息。https://wx.mail.qq.com/list/readtemplate?name=app_intro.html#/agreement/authorizationCode 官方的说明信息
6.2 服务配置
然后我们需要在自己的服务中配置对应的邮箱服务。
6.3 流程案例
然后我们来绘制相关的流程图来讲解。
部署流程后启动实例我们在相关的邮箱账号中就可以接收到相关的邮件信息: