1 SpringBoot-Activiti技术

1.1 技术介绍

  1. Activiti 7Alfresco经过实战考验的Activiti工作流引擎的演变,完全被采用在云环境中运行。它是根据 Cloud Native 应用程序概念构建的,与之前的Activiti版本在架构方面有所不同。
  2. 基于我的理解,帮助开发人员进行流程控制,而工作流的概念就是一个制度的体现,管理体系的体现。
  3. 举例来讲:小到家庭琐事,现在有个老李想和朋友,可是他身上没有钱,他需要得到老婆的同意,然后再由老婆拨款,才能出门和朋友吃饭。
  4. 更一步案例,现在老李正在读小学,马上要考试了,可是他今天生病了,需要请假,但是这次考试又很重要,他不想放弃。这时就是学校的管理制度起作用了,老师不仅同意了老李的病假,还为老李破例开通第二套试卷考试,老李在休息得当的情况下,顺利的完成了考试,考出高分。
  5. 而上升到公司,行业管理等等,其实也是人与人之间的确认信息。

· 较多使用场合
  1. 订单、报价处理、合同审核、客户电话处理、供应链管理。
  2. 土地开发审核、资源开发审核、人力资源、办公软件之类。

1.2 技术注意点

1.2.1 流程图

  1. 流程图必须有一个或多个默认流
  2. 流程判断必须带有参数条件
  3. 流程图的权限控制:assignee(不包括)、candidateUsers(哪些用户)、candidateGroups(哪些角色)

1.2.2 流程图ID

  1. 这个一定要修改,不能出现同名,否则就会出现这个错误。

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 环境准备

  1. SpringBoot2.0.4.RELEASE
  2. JDK1.8
  3. Activiti7.1.0.M2
  4. MySQL8.0.21
  5. 开发软件:IDEA
  6. 流程插件:actiBPM

1.5.2 安装插件

  1. Files Settings Plugins 搜索actiBPM 下载安装 搜索JBoss jBPM 下载安装

1.5.3 插件试玩

image.png

1.5.4 创建bpnmFile文件

image.png
创建好后,可以通过修改文件后缀名查看里面的代码
image.png
打开team01.xml
image.png

  1. <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  2. <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">
  3. <process id="myProcess_1" isClosed="false" isExecutable="true" processType="None">
  4. <extensionElements>
  5. <yaoqiang:description/>
  6. <yaoqiang:pageFormat height="841.8897637795276" imageableHeight="831.8897637795276" imageableWidth="588.1102362204724" imageableX="5.0" imageableY="5.0" orientation="0" width="598.1102362204724"/>
  7. <yaoqiang:page background="#FFFFFF" horizontalCount="1" verticalCount="1"/>
  8. </extensionElements>
  9. <startEvent id="_2" isInterrupting="true" name="StartEvent" parallelMultiple="false"/>
  10. <userTask activiti:candidateGroups="activitiTeam" activiti:exclusive="true" completionQuantity="1" id="_3" implementation="##unspecified" isForCompensation="false" name="填写申请单" startQuantity="1"/>
  11. <userTask activiti:candidateGroups="activitiTeam" activiti:exclusive="true" completionQuantity="1" id="_4" implementation="##unspecified" isForCompensation="false" name="审核" startQuantity="1"/>
  12. <endEvent id="_5" name="EndEvent"/>
  13. <sequenceFlow id="_6" sourceRef="_2" targetRef="_3"/>
  14. <sequenceFlow id="_7" sourceRef="_3" targetRef="_4"/>
  15. <sequenceFlow id="_8" sourceRef="_4" targetRef="_5"/>
  16. </process>
  17. <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">
  18. <bpmndi:BPMNPlane bpmnElement="myProcess_1">
  19. <bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
  20. <omgdc:Bounds height="32.0" width="32.0" x="65.0" y="80.0"/>
  21. <bpmndi:BPMNLabel>
  22. <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
  23. </bpmndi:BPMNLabel>
  24. </bpmndi:BPMNShape>
  25. <bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
  26. <omgdc:Bounds height="55.0" width="85.0" x="170.0" y="115.0"/>
  27. <bpmndi:BPMNLabel>
  28. <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
  29. </bpmndi:BPMNLabel>
  30. </bpmndi:BPMNShape>
  31. <bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
  32. <omgdc:Bounds height="55.0" width="85.0" x="310.0" y="155.0"/>
  33. <bpmndi:BPMNLabel>
  34. <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
  35. </bpmndi:BPMNLabel>
  36. </bpmndi:BPMNShape>
  37. <bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
  38. <omgdc:Bounds height="32.0" width="32.0" x="505.0" y="220.0"/>
  39. <bpmndi:BPMNLabel>
  40. <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
  41. </bpmndi:BPMNLabel>
  42. </bpmndi:BPMNShape>
  43. <bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6" sourceElement="_2" targetElement="_3">
  44. <omgdi:waypoint x="97.0" y="96.0"/>
  45. <omgdi:waypoint x="170.0" y="142.5"/>
  46. <bpmndi:BPMNLabel>
  47. <omgdc:Bounds height="18.96" width="6.0" x="130.5" y="109.77"/>
  48. </bpmndi:BPMNLabel>
  49. </bpmndi:BPMNEdge>
  50. <bpmndi:BPMNEdge bpmnElement="_7" id="BPMNEdge__7" sourceElement="_3" targetElement="_4">
  51. <omgdi:waypoint x="255.0" y="142.5"/>
  52. <omgdi:waypoint x="310.0" y="182.5"/>
  53. <bpmndi:BPMNLabel>
  54. <omgdc:Bounds height="18.96" width="6.0" x="279.5" y="153.02"/>
  55. </bpmndi:BPMNLabel>
  56. </bpmndi:BPMNEdge>
  57. <bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_4" targetElement="_5">
  58. <omgdi:waypoint x="395.0" y="182.5"/>
  59. <omgdi:waypoint x="505.0" y="236.0"/>
  60. <bpmndi:BPMNLabel>
  61. <omgdc:Bounds height="18.96" width="6.0" x="447.0" y="199.77"/>
  62. </bpmndi:BPMNLabel>
  63. </bpmndi:BPMNEdge>
  64. </bpmndi:BPMNPlane>
  65. </bpmndi:BPMNDiagram>
  66. </definitions>

将xml更改为png格式
image.png
image.png

1.5.5 创建activiti特有数据库

  1. 因为利用的是springboot,就不用使用xml配置,还要代码注入后,才能启动。

启动流程

yml配置 → ProcessEngine(流程引擎)→ RuntimeService(流程运行管理类)

  1. TaskService(流程运行管理类)
  2. RepositoryService(资源管理类)
  3. HistoryService(历史管理类)
  4. ManagementService(引擎管理类)
  5. 在此之前,你需要先知道Activiti7属于高版本,已经舍弃掉了三张表,这三张就是权限管理,由于过于简单舍弃,选择Spring Security来控制权限。
  6. 你必须在你的本地创建一个activiti的数据库,来存放新增的表。

2 实战例子

2.1 入门案例

2.1 创建pom

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.0.4.RELEASE</version>
  5. <relativePath/> <!-- lookup parent from repository -->
  6. </parent>
  7. <properties>
  8. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  9. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  10. <java.version>1.8</java.version>
  11. <mysql.version>8.0.21</mysql.version>
  12. </properties>
  13. <dependencies>
  14. <dependency>
  15. <groupId>org.springframework.boot</groupId>
  16. <artifactId>spring-boot-starter-web</artifactId>
  17. </dependency>
  18. <!-- activiti 自动建表 -->
  19. <dependency>
  20. <groupId>org.springframework.boot</groupId>
  21. <artifactId>spring-boot-starter-jdbc</artifactId>
  22. </dependency>
  23. <dependency>
  24. <groupId>org.activiti</groupId>
  25. <artifactId>activiti-spring-boot-starter</artifactId>
  26. <version>7.1.0.M2</version>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-starter-test</artifactId>
  31. <scope>test</scope>
  32. </dependency>
  33. <dependency>
  34. <groupId>mysql</groupId>
  35. <artifactId>mysql-connector-java</artifactId>
  36. <version>${mysql.version}</version>
  37. </dependency>
  38. <dependency>
  39. <groupId>org.projectlombok</groupId>
  40. <artifactId>lombok</artifactId>
  41. <optional>true</optional>
  42. </dependency>
  43. <!-- Thymeleaf依赖 -->
  44. <dependency>
  45. <groupId>org.springframework.boot</groupId>
  46. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  47. </dependency>
  48. <dependency>
  49. <groupId>org.projectlombok</groupId>
  50. <artifactId>lombok</artifactId>
  51. <version>RELEASE</version>
  52. <scope>compile</scope>
  53. </dependency>
  54. </dependencies>
  55. <build>
  56. <finalName>my-springboot-activiti-demo</finalName>
  57. <plugins>
  58. <plugin>
  59. <groupId>org.springframework.boot</groupId>
  60. <artifactId>spring-boot-maven-plugin</artifactId>
  61. </plugin>
  62. </plugins>
  63. </build>

2.2 准备application.yml

  1. spring:
  2. datasource:
  3. url: jdbc:mysql://localhost:3306/activiti
  4. username: root
  5. password: 123
  6. hikari:
  7. data-source-properties:
  8. useSSL: false
  9. serverTimezone: GMT+8
  10. useUnicode: true
  11. characterEncoding: utf8
  12. # 这个必须要加,否则 Activiti 自动建表会失败
  13. nullCatalogMeansCurrent: true
  14. activiti:
  15. # 保存历史数据级别设置为full最高级别,便于历史数据的追溯
  16. history-level: full
  17. db-history-used: true
  18. # 不启动检查activiti数据库版本是否匹配,提升应用启动效率
  19. database-schema-update: false
  20. #自动检查、部署流程定义文件
  21. check-process-definitions: true
  22. #流程定义文件存放目录,要具体到某个目录
  23. process-definition-location-prefix: classpath:/processes/
  24. # process-definition-location-suffixes: #流程文件格式
  25. # - **.bpmn20.xml
  26. # - **.bpmn

2.3 创建Spring Security的授权用户

  1. @Slf4j
  2. @Configuration
  3. @EnableWebSecurity
  4. public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  5. @Autowired
  6. @Override
  7. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  8. auth.userDetailsService(userDetailsService());
  9. }
  10. @Override
  11. protected void configure(HttpSecurity http) throws Exception {
  12. http
  13. .csrf().disable()
  14. .authorizeRequests()
  15. .anyRequest()
  16. .authenticated()
  17. .and()
  18. .httpBasic();
  19. }
  20. /**
  21. * spring security 高版本自带
  22. * @return
  23. */
  24. @Bean
  25. protected UserDetailsService myUserDetailsService() {
  26. InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
  27. // 四个普通用户,一个管理员
  28. String[][] usersGroupsAndRoles = {
  29. {"salaboy", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
  30. {"ryandawsonuk", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
  31. {"erdemedeiros", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
  32. {"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"},
  33. {"admin", "password", "ROLE_ACTIVITI_ADMIN"}};
  34. for (String[] user : usersGroupsAndRoles) {
  35. List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
  36. log.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]");
  37. inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]),
  38. authoritiesStrings.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())));
  39. }
  40. return inMemoryUserDetailsManager;
  41. }
  42. @Bean
  43. public PasswordEncoder passwordEncoder() {
  44. return new BCryptPasswordEncoder();
  45. }
  46. }

2.4 创建一个授权认证

注意:这只是测试环境,真是环境并不是怎么操作,还是需要用户正常登录授权,这里只是写了死值,方便测试。

  1. @Component
  2. @RequiredArgsConstructor(onConstructor_ = @Autowired)
  3. public class SecurityUtil {
  4. private final UserDetailsService userDetailsService;
  5. public void logInAs(String username) {
  6. UserDetails user = userDetailsService.loadUserByUsername(username);
  7. if (user == null) {
  8. throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user");
  9. }
  10. SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
  11. @Override
  12. public Collection<? extends GrantedAuthority> getAuthorities() {
  13. return user.getAuthorities();
  14. }
  15. @Override
  16. public Object getCredentials() {
  17. return user.getPassword();
  18. }
  19. @Override
  20. public Object getDetails() {
  21. return user;
  22. }
  23. @Override
  24. public Object getPrincipal() {
  25. return user;
  26. }
  27. @Override
  28. public boolean isAuthenticated() {
  29. return true;
  30. }
  31. @Override
  32. public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
  33. }
  34. @Override
  35. public String getName() {
  36. return user.getUsername();
  37. }
  38. }));
  39. org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
  40. }
  41. }

2.5 测试代码

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. public class SpringBootDemoActivitiApplicationTests {
  4. @Autowired
  5. private ProcessRuntime processRuntime; // 流程引擎的抽象,可以获取所有的服务
  6. /**
  7. * 列出所有流程定义
  8. */
  9. @Test
  10. public void contextLoads() {
  11. // 调用授权
  12. securityUtil.logInAs("salaboy");
  13. // 查询十条流程实例信息
  14. Page<ProcessDefinition> processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 10));
  15. processDefinitionPage.getContent().forEach(System.out::println);
  16. }
  17. }

2.6 结果

image.png

查询出来的结果
image.png
这里查询出的数据,对应的数据库中的act_re_procdef(流程定义数据表)。
image.png
其实,springboot只要把pom依赖导入,yml数据源配置好后,然后启动springbootapplication服务,数据库就能自动创建,而我们选择使用一个小案例,能够看到具体的调用流程实现。

当我启动springboot服务时,以下四张表会新增数据。

  • act_ge_property(属性数据表)

image.png

  • act_re_deployment(部署信息表)

image.png

  • act_ge_bytearray(二进制数据表)

image.png

  • act_re_procdef(流程定义数据表)

image.png

image.png

2.2 简单审批案例

image.png

详细图解流程运行过程
image.png

2.2.1 发起请假申请流程

  1. @Test
  2. public void testProcessInstance(){
  3. // 流程名
  4. String key = "bohui";
  5. // 申请人
  6. String username = "zhangsan";
  7. HashMap<String, Object> map = new HashMap<>();
  8. // 启动流程实例时给变量赋值
  9. map.put("assignee1", username);
  10. // 启动流程,发送请假请求
  11. ActivitiUtil.startProcessInstanceWithVariables(username, key,"请假", map);
  12. }
  1. /**
  2. * 启动流程
  3. * @param username 用户名
  4. * @param processKey 流程 Key => 对应bpmn文件里的id
  5. * @param processName 流程实例名
  6. * @param variables 变量map
  7. */
  8. public static void startProcessInstanceWithVariables(String username,
  9. String processKey, String processName,
  10. HashMap<String,Object> variables) {
  11. // Security 授权用户(这里的代码上面的授权认证一样)
  12. activitiUtil.securityUtil.logInAs(username);
  13. // 创建一个运行时流程,并启动该流程
  14. ProcessInstance processInstance = activitiUtil.processRuntime
  15. .start(ProcessPayloadBuilder
  16. .start() // 启动流程
  17. .withProcessDefinitionKey(processKey) // 流程运行时定义流程实例
  18. .withName(processName) // 流程事务,要做什么
  19. .withVariables(variables) // 流程变量,一些额外的属性
  20. .build());
  21. logger.info("流程实例启动成功: " + processInstance);
  22. }

执行结果:

  1. 流程实例启动成功: 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'}
  2. **数据库新增表**
  1. act_hi_actinst(历史节点表)

image.png

  1. act_hi_detail(历史详情表)

image.png

  1. act_hi_identitylink(历史流程人员表)

image.png

  1. act_hi_procinst(历史流程实例表)

image.png

  1. act_hi_taskinst(历史任务实例表)

image.png

  1. act_hi_varinst(历史变量表)

image.png

  1. act_ru_execution(运行时流程执行实例表)

image.png

  1. act_ru_identitylink(运行时流程人员表)

image.png

  1. act_ru_task(运行时任务节点表)

image.png

  1. act_ru_variable(运行时流程变量数据表)

image.png
image.png

2.2.2 获取所有流程定义

  1. @Test
  2. public void testQueryTask(){
  3. String assignee = "zhangsan";
  4. // 获取所有流程定义
  5. ActivitiUtil.printTaskList(assignee, 0, 10);
  6. }
  1. /**
  2. * 打印指派人所有任务
  3. *
  4. * @param assignee 指派人
  5. * @param startNum 分页开始下标
  6. * @param endNum 分页结束下标
  7. */
  8. public static void printTaskList(String assignee, Integer startNum, Integer endNum) {
  9. // 获取所有任务list
  10. Page<org.activiti.api.task.model.Task> tasks = getTaskList(assignee, startNum, endNum);
  11. // 任务的总数大于0
  12. if (tasks.getTotalItems() > 0) {
  13. // 有任务时,完成任务
  14. for (org.activiti.api.task.model.Task task : tasks.getContent()) {
  15. logger.info("任务: " + task);
  16. }
  17. }
  18. }
  1. @Autowired
  2. private CustomTaskRuntimeImpl customTaskRuntimeImpl;
  3. private static ActivitiUtil activitiUtil;
  4. /**
  5. * 查询当前指派人的任务
  6. *
  7. * @param assignee 指派人
  8. * @param startNum 分页开始下标
  9. * @param endNum 分页结束下标
  10. * @return 任务list
  11. */
  12. public static Page<org.activiti.api.task.model.Task> getTaskList(String assignee, Integer startNum, Integer endNum) {
  13. // 授权用户
  14. activitiUtil.securityUtil.logInAs(assignee);
  15. //
  16. return activitiUtil.customTaskRuntimeImpl.tasks(Pageable.of(startNum, endNum));
  17. }
  1. @PreAuthorize("hasRole('ACTIVITI_USER')") // 控制一个方法需要权限调用
  2. @Component
  3. public class CustomTaskRuntimeImpl implements TaskRuntime {
  4. // 安全管理器
  5. private final SecurityManager securityManager;
  6. // 流程运行管理类
  7. private final TaskService taskService;
  8. // API任务转换
  9. private final APITaskConverter taskConverter;
  10. /**
  11. *
  12. * @param pageable 可分页
  13. */
  14. @Override
  15. public Page<Task> tasks(Pageable pageable) {
  16. // 获取认证用户id
  17. String authenticatedUserId = securityManager.getAuthenticatedUserId();
  18. // 认证用户id不为空的场合
  19. if (authenticatedUserId != null && !authenticatedUserId.isEmpty()) {
  20. // 获取认证用户组
  21. List<String> userGroups = securityManager.getAuthenticatedUserGroups();
  22. // 分页的任务
  23. return tasks(pageable,
  24. TaskPayloadBuilder.tasks().withAssignee(authenticatedUserId).withGroups(userGroups).build());
  25. }
  26. throw new IllegalStateException("You need an authenticated user to perform a task query");
  27. }
  28. /**
  29. *
  30. * @param pageable 可分页
  31. * @param getTasksPayload 获取多个任务负载
  32. */
  33. @Override
  34. public Page<Task> tasks(Pageable pageable,
  35. GetTasksPayload getTasksPayload) {
  36. // 创建任务队列
  37. TaskQuery taskQuery = taskService.createTaskQuery();
  38. // 任务负载不为空的场合
  39. if (getTasksPayload == null) {
  40. // 生成任务负载
  41. getTasksPayload = TaskPayloadBuilder.tasks().build();
  42. }
  43. // 获取认证用户ID
  44. String authenticatedUserId = securityManager.getAuthenticatedUserId();
  45. // 认证用户ID不为空的场合
  46. if (authenticatedUserId != null && !authenticatedUserId.isEmpty()) {
  47. // 获取认证用户组
  48. List<String> userGroups = securityManager.getAuthenticatedUserGroups();
  49. // 任务负载设置指派人ID
  50. getTasksPayload.setAssigneeId(authenticatedUserId);
  51. // 任务负载设置用户组
  52. getTasksPayload.setGroups(userGroups);
  53. } else {
  54. throw new IllegalStateException("You need an authenticated user to perform a task query");
  55. }
  56. // 按照分配组 OR 指派人查询
  57. taskQuery = taskQuery.or()
  58. .taskCandidateOrAssigned(getTasksPayload.getAssigneeId(),
  59. getTasksPayload.getGroups())
  60. // .taskOwner(authenticatedUserId)
  61. .endOr();
  62. // 获取流程实例ID不为空的场合
  63. if (getTasksPayload.getProcessInstanceId() != null) {
  64. // 为任务队列设置流程实例ID
  65. taskQuery = taskQuery.processInstanceId(getTasksPayload.getProcessInstanceId());
  66. }
  67. // 获取父任务ID不为空的场合
  68. if (getTasksPayload.getParentTaskId() != null) {
  69. // 为任务队列设置父任务ID
  70. taskQuery = taskQuery.taskParentTaskId(getTasksPayload.getParentTaskId());
  71. }
  72. // 将任务队列里的分页list转换为任务list
  73. List<Task> tasks = taskConverter.from(taskQuery.listPage(pageable.getStartIndex(),
  74. pageable.getMaxItems()));
  75. // 返回分页实现类
  76. return new PageImpl<>(tasks,
  77. Math.toIntExact(taskQuery.count()));
  78. }
  79. }

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}

2.2.3 进入审批阶段(指定审批人)

  1. @Test
  2. public void testCompleteTask(){
  3. // 申请人
  4. String assignee = "zhangsan";
  5. HashMap<String, Object> map = new HashMap<>();
  6. // 完成任务时同时指定之后流程的指派人(审批人)
  7. map.put("assignee2", "lisi");
  8. // 根据变量条件完成对应任务
  9. ActivitiUtil.completeTaskWithVariables(assignee, map);
  10. }
  1. /**
  2. * 完成任务
  3. *
  4. * @param assignee 指派人
  5. * @param variables 变量map
  6. */
  7. public static void completeTaskWithVariables(String assignee, HashMap<String,Object> variables) {
  8. // 获取默认前十条任务
  9. Page<org.activiti.api.task.model.Task> tasks = getTaskList(assignee, 0, 10);
  10. // 任务总数大于零的场合
  11. if (tasks.getTotalItems() > 0) {
  12. // 有任务时,完成任务
  13. for (org.activiti.api.task.model.Task task : tasks.getContent()) {
  14. System.out.println(task);
  15. // 完成任务
  16. activitiUtil.taskRuntime.complete(
  17. TaskPayloadBuilder.complete()
  18. .withTaskId(task.getId())
  19. .withVariables(variables).build());
  20. logger.info(assignee + "完成任务");
  21. }
  22. }
  23. }

执行结果:

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完成任务

数据表出现的变化
image.png

2.2.4 进入审批阶段(审批人不通过申请,驳回请求)

  1. @Test
  2. public void testCompleteTask2(){
  3. // 审批人
  4. String assignee = "lisi";
  5. HashMap<String, Object> map = new HashMap<>();
  6. // 完成任务时同时指定审核为驳回
  7. map.put("audit", false);
  8. // 根据变量条件完成对应任务
  9. ActivitiUtil.completeTaskWithVariables(assignee, map);
  10. }

执行结果

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完成任务

数据表出现的变化

image.png

2.2.5 进入审批阶段(申请人重新申请请求)

  1. @Test
  2. public void testCompleteTask3(){
  3. // 申请人
  4. String assignee = "zhangsan";
  5. // 重新提交任务
  6. ActivitiUtil.completeTask(assignee);
  7. }
  1. /**
  2. * 完成指派人所有任务
  3. *
  4. * @param assignee 指派人
  5. */
  6. public static void completeTask(String assignee) {
  7. // 获取默认前十条任务
  8. Page<org.activiti.api.task.model.Task> tasks = getTaskList(assignee, 0, 10);
  9. // 任务总数大于零的场合
  10. if (tasks.getTotalItems() > 0) {
  11. // 有任务时,完成任务
  12. for (org.activiti.api.task.model.Task task : tasks.getContent()) {
  13. System.out.println(task);
  14. // 完成任务
  15. activitiUtil.taskRuntime.complete(
  16. TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
  17. logger.info(assignee + "完成任务");
  18. }
  19. }
  20. }

2.2.6 进入审批阶段(审批通过)

  1. @Test
  2. public void testCompleteTask4(){
  3. // 审批人
  4. String assignee = "lisi";
  5. HashMap<String, Object> map = new HashMap<>();
  6. // 完成任务时同时指定审核为通过
  7. map.put("audit", true);
  8. // 根据变量条件完成对应任务
  9. ActivitiUtil.completeTaskWithVariables(assignee, map);
  10. }

执行结果

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完成任务

数据表出现的变化

image.png