15.SpringBoot-Activiti技术
1.1 技术介绍
Activiti 7是Alfresco经过实战考验的Activiti工作流引擎的演变,完全被采用在云环境中运行。它是根据 Cloud Native 应用程序概念构建的,与之前的Activiti版本在架构方面有所不同。
基于我的理解,帮助开发人员进行流程控制,而工作流的概念就是一个制度的体现,管理体系的体现。
举例来讲:小到家庭琐事,现在有个老李想和朋友,可是他身上没有钱,他需要得到老婆的同意,然后再由老婆拨款,才能出门和朋友吃饭。
更一步案例,现在老李正在读小学,马上要考试了,可是他今天生病了,需要请假,但是这次考试又很重要,他不想放弃。这时就是学校的管理制度起作用了,老师不仅同意了老李的病假,还为老李破例开通第二套试卷考试,老李在休息得当的情况下,顺利的完成了考试,考出高分。
而上升到公司,行业管理等等,其实也是人与人之间的确认信息。
· 较多使用场合
订单、报价处理、合同审核、客户电话处理、供应链管理。
土地开发审核、资源开发审核、人力资源、办公软件之类。
1.2 技术注意点
1.2.1 流程图
- 流程图必须有一个或多个默认流
- 流程判断必须带有参数条件
- 流程图的权限控制:assignee(不包括)、candidateUsers(哪些用户)、candidateGroups(哪些角色)
1.2.2 流程图ID
这个一定要修改,不能出现同名,否则就会出现这个错误。
The deployment contains process definitions with the same key (process id attribute), this is not…
https://blog.csdn.net/qq_41520636/article/details/118304495
1.3 技术具体demo文件路径
1.4 demo的UML图
1.5 demo的技术代码实现
1.5.1 环境准备
SpringBoot:2.0.4.RELEASE
JDK:1.8
Activiti:7.1.0.M2
MySQL:8.0.21
开发软件:IDEA
流程插件:actiBPM
1.5.2 安装插件
Files → Settings → Plugins → 搜索actiBPM → 下载安装 → 搜索JBoss jBPM → 下载安装
1.5.3 插件试玩
创建bpnmFile文件
创建好后,可以通过修改文件后缀名查看里面的代码
打开team01.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 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" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:yaoqiang="http://bpmn.sourceforge.net" expressionLanguage="http://www.w3.org/1999/XPath" id="m1544167269809" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="myProcess_1" isClosed="false" isExecutable="true" processType="None">
<extensionElements>
<yaoqiang:description/>
<yaoqiang:pageFormat height="841.8897637795276" imageableHeight="831.8897637795276" imageableWidth="588.1102362204724" imageableX="5.0" imageableY="5.0" orientation="0" width="598.1102362204724"/>
<yaoqiang:page background="#FFFFFF" horizontalCount="1" verticalCount="1"/>
</extensionElements>
<startEvent id="_2" isInterrupting="true" name="StartEvent" parallelMultiple="false"/>
<userTask activiti:candidateGroups="activitiTeam" activiti:exclusive="true" completionQuantity="1" id="_3" implementation="##unspecified" isForCompensation="false" name="填写申请单" startQuantity="1"/>
<userTask activiti:candidateGroups="activitiTeam" activiti:exclusive="true" completionQuantity="1" id="_4" implementation="##unspecified" isForCompensation="false" name="审核" startQuantity="1"/>
<endEvent id="_5" name="EndEvent"/>
<sequenceFlow id="_6" sourceRef="_2" targetRef="_3"/>
<sequenceFlow id="_7" sourceRef="_3" targetRef="_4"/>
<sequenceFlow id="_8" sourceRef="_4" targetRef="_5"/>
</process>
<bpmndi:BPMNDiagram documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
<bpmndi:BPMNPlane bpmnElement="myProcess_1">
<bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
<omgdc:Bounds height="32.0" width="32.0" x="65.0" y="80.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
<omgdc:Bounds height="55.0" width="85.0" x="170.0" y="115.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
<omgdc:Bounds height="55.0" width="85.0" x="310.0" y="155.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
<omgdc:Bounds height="32.0" width="32.0" x="505.0" y="220.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6" sourceElement="_2" targetElement="_3">
<omgdi:waypoint x="97.0" y="96.0"/>
<omgdi:waypoint x="170.0" y="142.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="18.96" width="6.0" x="130.5" y="109.77"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_7" id="BPMNEdge__7" sourceElement="_3" targetElement="_4">
<omgdi:waypoint x="255.0" y="142.5"/>
<omgdi:waypoint x="310.0" y="182.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="18.96" width="6.0" x="279.5" y="153.02"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_4" targetElement="_5">
<omgdi:waypoint x="395.0" y="182.5"/>
<omgdi:waypoint x="505.0" y="236.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="18.96" width="6.0" x="447.0" y="199.77"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
将xml更改为png格式
1.5.4 创建activiti特有数据库
因为利用的是springboot,就不用使用xml配置,还要代码注入后,才能启动。
启动流程
yml配置 → ProcessEngine(流程引擎)→ RuntimeService(流程运行管理类)
→ TaskService(流程运行管理类)
→ RepositoryService(资源管理类)
→ HistoryService(历史管理类)
→ ManagementService(引擎管理类)
在此之前,你需要先知道Activiti7属于高版本,已经舍弃掉了三张表,这三张就是权限管理,由于过于简单舍弃,选择Spring Security来控制权限。
你必须在你的本地创建一个activiti的数据库,来存放新增的表。
实战例子
1.创建pom
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mysql.version>8.0.21</mysql.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- activiti 自动建表 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Thymeleaf依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>my-springboot-activiti-demo</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.准备application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/activiti
username: root
password: 123
hikari:
data-source-properties:
useSSL: false
serverTimezone: GMT+8
useUnicode: true
characterEncoding: utf8
# 这个必须要加,否则 Activiti 自动建表会失败
nullCatalogMeansCurrent: true
activiti:
# 保存历史数据级别设置为full最高级别,便于历史数据的追溯
history-level: full
db-history-used: true
# 不启动检查activiti数据库版本是否匹配,提升应用启动效率
database-schema-update: false
#自动检查、部署流程定义文件
check-process-definitions: true
#流程定义文件存放目录,要具体到某个目录
process-definition-location-prefix: classpath:/processes/
# process-definition-location-suffixes: #流程文件格式
# - **.bpmn20.xml
# - **.bpmn
3.创建Spring Security的授权用户
@Slf4j
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
/**
* spring security 高版本自带
* @return
*/
@Bean
protected UserDetailsService myUserDetailsService() {
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
// 四个普通用户,一个管理员
String[][] usersGroupsAndRoles = {
{"salaboy", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"ryandawsonuk", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"erdemedeiros", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"},
{"admin", "password", "ROLE_ACTIVITI_ADMIN"}};
for (String[] user : usersGroupsAndRoles) {
List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
log.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]");
inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]),
authoritiesStrings.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())));
}
return inMemoryUserDetailsManager;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
4.创建一个授权认证
注意:这只是测试环境,真是环境并不是怎么操作,还是需要用户正常登录授权,这里只是写了死值,方便测试。
@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class SecurityUtil {
private final UserDetailsService userDetailsService;
public void logInAs(String username) {
UserDetails user = userDetailsService.loadUserByUsername(username);
if (user == null) {
throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user");
}
SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
}
@Override
public Object getCredentials() {
return user.getPassword();
}
@Override
public Object getDetails() {
return user;
}
@Override
public Object getPrincipal() {
return user;
}
@Override
public boolean isAuthenticated() {
return true;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
@Override
public String getName() {
return user.getUsername();
}
}));
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
}
}
5.测试代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootDemoActivitiApplicationTests {
@Autowired
private ProcessRuntime processRuntime; // 流程引擎的抽象,可以获取所有的服务
/**
* 列出所有流程定义
*/
@Test
public void contextLoads() {
// 调用授权
securityUtil.logInAs("salaboy");
// 查询十条流程实例信息
Page<ProcessDefinition> processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 10));
processDefinitionPage.getContent().forEach(System.out::println);
}
}
6.结果
查询出来的结果
这里查询出的数据,对应的数据库中的act_re_procdef(流程定义数据表)。
其实,springboot只要把pom依赖导入,yml数据源配置好后,然后启动springbootapplication服务,数据库就能自动创建,而我们选择使用一个小案例,能够看到具体的调用流程实现。
当我启动springboot服务时,以下四张表会新增数据。
act_ge_property(属性数据表)
act_re_deployment(部署信息表)
act_ge_bytearray(二进制数据表)
act_re_procdef(流程定义数据表)
1.5.5 简单审批案例
详细图解流程运行过程
1.发起请假申请流程
@Test
public void testProcessInstance(){
// 流程名
String key = "bohui";
// 申请人
String username = "zhangsan";
HashMap<String, Object> map = new HashMap<>();
// 启动流程实例时给变量赋值
map.put("assignee1", username);
// 启动流程,发送请假请求
ActivitiUtil.startProcessInstanceWithVariables(username, key,"请假", map);
}
/**
* 启动流程
* @param username 用户名
* @param processKey 流程 Key => 对应bpmn文件里的id
* @param processName 流程实例名
* @param variables 变量map
*/
public static void startProcessInstanceWithVariables(String username,
String processKey, String processName,
HashMap<String,Object> variables) {
// Security 授权用户(这里的代码上面的授权认证一样)
activitiUtil.securityUtil.logInAs(username);
// 创建一个运行时流程,并启动该流程
ProcessInstance processInstance = activitiUtil.processRuntime
.start(ProcessPayloadBuilder
.start() // 启动流程
.withProcessDefinitionKey(processKey) // 流程运行时定义流程实例
.withName(processName) // 流程事务,要做什么
.withVariables(variables) // 流程变量,一些额外的属性
.build());
logger.info("流程实例启动成功: " + processInstance);
}
执行结果:
流程实例启动成功: ProcessInstance{id=‘1bac6242-d8e3-11eb-abf7-04d3b0ccfa07’, name=‘请假’, processDefinitionId=‘bohui:1:e2f71a3c-d8bf-11eb-9c08-04d3b0ccfa07’, processDefinitionKey=‘bohui’, parentId=‘null’, initiator=‘zhangsan’, startDate=Tue Jun 29 22:05:49 CST 2021, businessKey=‘null’, status=RUNNING, processDefinitionVersion=‘1’}
数据库新增表
act_hi_actinst(历史节点表)
act_hi_detail(历史详情表)
act_hi_identitylink(历史流程人员表)
act_hi_procinst(历史流程实例表)
act_hi_taskinst(历史任务实例表)
act_hi_varinst(历史变量表)
act_ru_execution(运行时流程执行实例表)
act_ru_identitylink(运行时流程人员表)
act_ru_task(运行时任务节点表)
act_ru_variable(运行时流程变量数据表)
2.获取所有流程定义
@Test
public void testQueryTask(){
String assignee = "zhangsan";
// 获取所有流程定义
ActivitiUtil.printTaskList(assignee, 0, 10);
}
/**
* 打印指派人所有任务
*
* @param assignee 指派人
* @param startNum 分页开始下标
* @param endNum 分页结束下标
*/
public static void printTaskList(String assignee, Integer startNum, Integer endNum) {
// 获取所有任务list
Page<org.activiti.api.task.model.Task> tasks = getTaskList(assignee, startNum, endNum);
// 任务的总数大于0
if (tasks.getTotalItems() > 0) {
// 有任务时,完成任务
for (org.activiti.api.task.model.Task task : tasks.getContent()) {
logger.info("任务: " + task);
}
}
}
@Autowired
private CustomTaskRuntimeImpl customTaskRuntimeImpl;
private static ActivitiUtil activitiUtil;
/**
* 查询当前指派人的任务
*
* @param assignee 指派人
* @param startNum 分页开始下标
* @param endNum 分页结束下标
* @return 任务list
*/
public static Page<org.activiti.api.task.model.Task> getTaskList(String assignee, Integer startNum, Integer endNum) {
// 授权用户
activitiUtil.securityUtil.logInAs(assignee);
//
return activitiUtil.customTaskRuntimeImpl.tasks(Pageable.of(startNum, endNum));
}
@PreAuthorize("hasRole('ACTIVITI_USER')") // 控制一个方法需要权限调用
@Component
public class CustomTaskRuntimeImpl implements TaskRuntime {
// 安全管理器
private final SecurityManager securityManager;
// 流程运行管理类
private final TaskService taskService;
// API任务转换
private final APITaskConverter taskConverter;
/**
*
* @param pageable 可分页
*/
@Override
public Page<Task> tasks(Pageable pageable) {
// 获取认证用户id
String authenticatedUserId = securityManager.getAuthenticatedUserId();
// 认证用户id不为空的场合
if (authenticatedUserId != null && !authenticatedUserId.isEmpty()) {
// 获取认证用户组
List<String> userGroups = securityManager.getAuthenticatedUserGroups();
// 分页的任务
return tasks(pageable,
TaskPayloadBuilder.tasks().withAssignee(authenticatedUserId).withGroups(userGroups).build());
}
throw new IllegalStateException("You need an authenticated user to perform a task query");
}
/**
*
* @param pageable 可分页
* @param getTasksPayload 获取多个任务负载
*/
@Override
public Page<Task> tasks(Pageable pageable,
GetTasksPayload getTasksPayload) {
// 创建任务队列
TaskQuery taskQuery = taskService.createTaskQuery();
// 任务负载不为空的场合
if (getTasksPayload == null) {
// 生成任务负载
getTasksPayload = TaskPayloadBuilder.tasks().build();
}
// 获取认证用户ID
String authenticatedUserId = securityManager.getAuthenticatedUserId();
// 认证用户ID不为空的场合
if (authenticatedUserId != null && !authenticatedUserId.isEmpty()) {
// 获取认证用户组
List<String> userGroups = securityManager.getAuthenticatedUserGroups();
// 任务负载设置指派人ID
getTasksPayload.setAssigneeId(authenticatedUserId);
// 任务负载设置用户组
getTasksPayload.setGroups(userGroups);
} else {
throw new IllegalStateException("You need an authenticated user to perform a task query");
}
// 按照分配组 OR 指派人查询
taskQuery = taskQuery.or()
.taskCandidateOrAssigned(getTasksPayload.getAssigneeId(),
getTasksPayload.getGroups())
// .taskOwner(authenticatedUserId)
.endOr();
// 获取流程实例ID不为空的场合
if (getTasksPayload.getProcessInstanceId() != null) {
// 为任务队列设置流程实例ID
taskQuery = taskQuery.processInstanceId(getTasksPayload.getProcessInstanceId());
}
// 获取父任务ID不为空的场合
if (getTasksPayload.getParentTaskId() != null) {
// 为任务队列设置父任务ID
taskQuery = taskQuery.taskParentTaskId(getTasksPayload.getParentTaskId());
}
// 将任务队列里的分页list转换为任务list
List<Task> tasks = taskConverter.from(taskQuery.listPage(pageable.getStartIndex(),
pageable.getMaxItems()));
// 返回分页实现类
return new PageImpl<>(tasks,
Math.toIntExact(taskQuery.count()));
}
}
Activiti 开发案例之 API 映射 SQL 查询
https://www.imooc.com/article/279591
查询结果:
任务: TaskImpl{id=‘1bae8529-d8e3-11eb-abf7-04d3b0ccfa07’, owner=‘null’, assignee=‘zhangsan’, name=‘发起’, description=‘null’, createdDate=Tue Jun 29 22:05:49 CST 2021, claimedDate=null, dueDate=null, priority=50, processDefinitionId=‘bohui:1:e2f71a3c-d8bf-11eb-9c08-04d3b0ccfa07’, processInstanceId=‘1bac6242-d8e3-11eb-abf7-04d3b0ccfa07’, parentTaskId=‘null’, formKey=‘null’, status=ASSIGNED, processDefinitionVersion=null, businessKey=null, taskDefinitionKey=_3}
3.进入审批阶段(指定审批人)
@Test
public void testCompleteTask(){
// 申请人
String assignee = "zhangsan";
HashMap<String, Object> map = new HashMap<>();
// 完成任务时同时指定之后流程的指派人(审批人)
map.put("assignee2", "lisi");
// 根据变量条件完成对应任务
ActivitiUtil.completeTaskWithVariables(assignee, map);
}
/**
* 完成任务
*
* @param assignee 指派人
* @param variables 变量map
*/
public static void completeTaskWithVariables(String assignee, HashMap<String,Object> variables) {
// 获取默认前十条任务
Page<org.activiti.api.task.model.Task> tasks = getTaskList(assignee, 0, 10);
// 任务总数大于零的场合
if (tasks.getTotalItems() > 0) {
// 有任务时,完成任务
for (org.activiti.api.task.model.Task task : tasks.getContent()) {
System.out.println(task);
// 完成任务
activitiUtil.taskRuntime.complete(
TaskPayloadBuilder.complete()
.withTaskId(task.getId())
.withVariables(variables).build());
logger.info(assignee + "完成任务");
}
}
}
执行结果:
Logged in as: zhangsan
TaskImpl{id=‘1bae8529-d8e3-11eb-abf7-04d3b0ccfa07’, owner=‘null’, assignee=‘zhangsan’, name=‘发起’, description=‘null’, createdDate=Tue Jun 29 22:05:49 CST 2021, claimedDate=null, dueDate=null, priority=50, processDefinitionId=‘bohui:1:e2f71a3c-d8bf-11eb-9c08-04d3b0ccfa07’, processInstanceId=‘1bac6242-d8e3-11eb-abf7-04d3b0ccfa07’, parentTaskId=‘null’, formKey=‘null’, status=ASSIGNED, processDefinitionVersion=null, businessKey=null, taskDefinitionKey=_3}
2021-06-30 00:40:35.753 INFO 16368 — [ main] c.z.a.utils.ActivitiUtil : zhangsan完成任务
数据表出现的变化
4.进入审批阶段(审批人不通过申请,驳回请求)
@Test
public void testCompleteTask2(){
// 审批人
String assignee = "lisi";
HashMap<String, Object> map = new HashMap<>();
// 完成任务时同时指定审核为驳回
map.put("audit", false);
// 根据变量条件完成对应任务
ActivitiUtil.completeTaskWithVariables(assignee, map);
}
执行结果:
Logged in as: lisi
TaskImpl{id=‘ba78a61b-d8f8-11eb-bb2a-04d3b0ccfa07’, owner=‘null’, assignee=‘lisi’, name=‘审核’, description=‘null’, createdDate=Wed Jun 30 00:40:35 CST 2021, claimedDate=null, dueDate=null, priority=50, processDefinitionId=‘bohui:1:e2f71a3c-d8bf-11eb-9c08-04d3b0ccfa07’, processInstanceId=‘1bac6242-d8e3-11eb-abf7-04d3b0ccfa07’, parentTaskId=‘null’, formKey=‘null’, status=ASSIGNED, processDefinitionVersion=null, businessKey=null, taskDefinitionKey=_4}
2021-06-30 00:45:05.008 INFO 1940 — [ main] c.z.a.utils.ActivitiUtil : lisi完成任务
数据表出现的变化
5.进入审批阶段(申请人重新申请请求)
@Test
public void testCompleteTask3(){
// 申请人
String assignee = "zhangsan";
// 重新提交任务
ActivitiUtil.completeTask(assignee);
}
/**
* 完成指派人所有任务
*
* @param assignee 指派人
*/
public static void completeTask(String assignee) {
// 获取默认前十条任务
Page<org.activiti.api.task.model.Task> tasks = getTaskList(assignee, 0, 10);
// 任务总数大于零的场合
if (tasks.getTotalItems() > 0) {
// 有任务时,完成任务
for (org.activiti.api.task.model.Task task : tasks.getContent()) {
System.out.println(task);
// 完成任务
activitiUtil.taskRuntime.complete(
TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
logger.info(assignee + "完成任务");
}
}
}
6.进入审批阶段(审批通过)
@Test
public void testCompleteTask4(){
// 审批人
String assignee = "lisi";
HashMap<String, Object> map = new HashMap<>();
// 完成任务时同时指定审核为通过
map.put("audit", true);
// 根据变量条件完成对应任务
ActivitiUtil.completeTaskWithVariables(assignee, map);
}
执行结果:
Logged in as: lisi
TaskImpl{id=‘d0cddac7-d8f9-11eb-8c91-04d3b0ccfa07’, owner=‘null’, assignee=‘lisi’, name=‘审核’, description=‘null’, createdDate=Wed Jun 30 00:48:22 CST 2021, claimedDate=null, dueDate=null, priority=50, processDefinitionId=‘bohui:1:e2f71a3c-d8bf-11eb-9c08-04d3b0ccfa07’, processInstanceId=‘1bac6242-d8e3-11eb-abf7-04d3b0ccfa07’, parentTaskId=‘null’, formKey=‘null’, status=ASSIGNED, processDefinitionVersion=null, businessKey=null, taskDefinitionKey=_4}
2021-06-30 00:48:44.667 INFO 9616 — [ main] c.z.a.utils.ActivitiUtil : lisi完成任务
数据表出现的变化
完结!原创不易,点个赞再走!