• 2022-11-14 西安 activiti工作流(01)


    语言确实有其局限性,但我相信:一件值得做的事情即使做的不怎么样也是值得的!

    概念

    1.流程审批以前的实现方式

    在没有专门的工作流引擎之前,为了实现流程控制,通常的做法就是采用状态字段的值来跟踪流程的变化情况。通过状态字段的取值来决定记录是否显示。

    缺点:耦合性太高

    通过状态字段虽然做到了流程控制,但是当我们的流程发生变更的时候,这种方式所编写的代码也要进行调整。


    2.activiti工作流引擎

    官方网站:https://www.activiti.org/

    业务系统和流程系统剥离

    Activiti是一个工作流引擎,activiti可以将业务系统中复杂的业务流程抽取出来。

    使用专门的建模语言BPMN2.O进行定义,业务流程按照预先定义的流程进行执行,实现了系统的流程由activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。


    3.BPM和BPMN

    BPM(Business Process Management)即业务流程管理,是一种规范化的构造端到端的业务流程

    BPMN(Business Process Model AndNotation)-业务流程模型和符号是由BPMl(BusinessProcess
    Management Initiative)开发的一套标准的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。

    Bpmn图形其实是通过XML表示业务流程,流程图其实就是一个XML,由流程设计器读取这XML,显示为图形化界面


    activiti的使用

    1.activiti部署

    Activiti是一个工作流引擎(其实就是一堆jar包API),业务系统访问activiti的接口,就可以方便的操作流程相关数据,这样就可以把工作流环境与业务系统的环境集成在一起


    2.流程定义器

    使用activiti流程建模工具(activity-designer)定义业务流程(.bpmn文件)。.bpmn文件就是业务流程定义文件,通过xml定义业务流程。


    3.流程存储数据库

    activiti部署业务流程定义(.bpmn文件)时:
    需要使用activiti提供的api把流程定义内容存储在数据库中,在activiti执行过程中可以查询定义的内容


    4.启动流程实例

    流程实例也叫:ProcessInstance
    启动一个流程实例表示开始一次业务流程的运行

    注意:多个流程实例之间互相不影响


    5.查询和办理任务

    系统的业务流程交给activiti管理,通过activiti就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些activiti帮我们管理了,而不需要开发人员自己编写sq语句查询。

    用户办理任务:用户查询待办任务后,就可以办理某个任务


    6.流程结束

    当任务办理完成没有下一个任务结点了,这个流程实例就完成了。


    activiti整合SpringBoot环境搭建

    1.下载activitiBPM插件

    actiBPM - IntelliJ IDEs Plugin | Marketplace

    点击下载

    打开idea的插件

    在resource/processes创建bpmn文件

    定义流程,按照BPMN的规范,使用流程定义工具,用流程符号把整个流程描述出来


    2.POM文件引入maven依赖

    pom.xml文件全部内容如下

    1. "1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    5. <modelVersion>4.0.0modelVersion>
    6. <groupId>com.xymgroupId>
    7. <artifactId>activiti-projectartifactId>
    8. <version>1.0-SNAPSHOTversion>
    9. <parent>
    10. <groupId>org.springframework.bootgroupId>
    11. <artifactId>spring-boot-starter-parentartifactId>
    12. <version>2.1.0.RELEASEversion>
    13. parent>
    14. <dependencies>
    15. <dependency>
    16. <groupId>org.springframework.bootgroupId>
    17. <artifactId>spring-boot-starter-webartifactId>
    18. dependency>
    19. <dependency>
    20. <groupId>org.springframework.bootgroupId>
    21. <artifactId>spring-boot-starter-jdbcartifactId>
    22. dependency>
    23. <dependency>
    24. <groupId>org.springframework.bootgroupId>
    25. <artifactId>spring-boot-starter-testartifactId>
    26. dependency>
    27. <dependency>
    28. <groupId>org.activitigroupId>
    29. <artifactId>activiti-spring-boot-starterartifactId>
    30. <version>7.0.0.Beta2version>
    31. dependency>
    32. <dependency>
    33. <groupId>mysqlgroupId>
    34. <artifactId>mysql-connector-javaartifactId>
    35. dependency>
    36. <dependency>
    37. <groupId>org.projectlombokgroupId>
    38. <artifactId>lombokartifactId>
    39. dependency>
    40. dependencies>
    41. <build>
    42. <plugins>
    43. <plugin>
    44. <groupId>org.springframework.bootgroupId>
    45. <artifactId>spring-boot-maven-pluginartifactId>
    46. plugin>
    47. plugins>
    48. build>
    49. project>

    3.application.yml配置文件

    1. spring:
    2. datasource:
    3. driver-class-name: com.mysql.jdbc.Driver
    4. url: jdbc:mysql:///actspringboot?useUnicode=true&characterEconding=utf8&serverTimezone=GMT
    5. username: root
    6. password: 123456
    7. activiti:
    8. #true:表不存在会自动创建
    9. database-schema-update: true
    10. #开启历史表
    11. db-history-used: true
    12. #保存历史数据的最高级别(会保存流程相关的细节数据)
    13. history-level: full

    4.SecurityUtil 和 DemoApplicationConfig 

    1. /**
    2. * @Description:快速实现SpringSecurity安全框架的配置
    3. * @Author: xiaoyumao
    4. * @Date: 2022/11/25 16:21
    5. */
    6. public class SecurityUtil {
    7. private Logger logger = LoggerFactory.getLogger(SecurityUtil.class);
    8. @Autowired
    9. @Qualifier("myUserDetailsService")
    10. private UserDetailsService userDetailsService;
    11. public void logInAs(String username) {
    12. UserDetails user = userDetailsService.loadUserByUsername(username);
    13. if (user == null) {
    14. throw new RuntimeException("用户" + username + "不存在");
    15. }
    16. logger.info(">Logged in as: " + username);
    17. SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
    18. @Override
    19. public Collectionextends GrantedAuthority> getAuthorities() {
    20. return user.getAuthorities();
    21. }
    22. @Override
    23. public Object getCredentials() {
    24. return user.getPassword();
    25. }
    26. @Override
    27. public Object getDetails() {
    28. return user;
    29. }
    30. @Override
    31. public Object getPrincipal() {
    32. return user;
    33. }
    34. @Override
    35. public boolean isAuthenticated() {
    36. return true;
    37. }
    38. @Override
    39. public void setAuthenticated(boolean b) throws IllegalArgumentException {
    40. }
    41. @Override
    42. public String getName() {
    43. return user.getUsername();
    44. }
    45. }));
    46. org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
    47. }
    48. }
    1. /**
    2. * @Description: 实现SpringSecurity框架的用户权限的配置
    3. * @Author: xiaoyumao
    4. * @Date: 2022/11/25 16:39
    5. */
    6. @Slf4j
    7. @Configuration
    8. public class DemoApplicationConfig {
    9. private Logger logger = LoggerFactory.getLogger(DemoApplicationConfig.class);
    10. @Bean
    11. public UserDetailsService myUserDetailsService() {
    12. //把用户存储在内存中
    13. InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
    14. //这里添加用户,后面处理流程用到的任务负责人在这里添加
    15. String[][] usersGroupsAndRoles = {
    16. {"jack", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
    17. {"rose", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
    18. {"tom", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
    19. {"other", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
    20. {"system", "password", "ROLE_ACTIVITI_USER"},
    21. {"admin", "password", "ROLE_ACTIVITI_USER"},
    22. };
    23. for (String[] user : usersGroupsAndRoles) {
    24. //把用户的角色和组拿出来
    25. List authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
    26. logger.info(">Registering new user: " + user[0] + "with the following Authorities[" + authoritiesStrings + "]");
    27. inMemoryUserDetailsManager.createUser(new User(
    28. user[0],
    29. passwordEncoder().encode(user[1]),
    30. authoritiesStrings.stream().map(str -> new SimpleGrantedAuthority(str)).collect(Collectors.toList())));
    31. }
    32. return inMemoryUserDetailsManager;
    33. }
    34. @Bean
    35. public PasswordEncoder passwordEncoder() {
    36. return new BCryptPasswordEncoder();
    37. }
    38. }

    5.测试类-部署/查询流程

    activiti7可以自动部署流程,前提是在resources目录下创建一个新的目录processes,用来放置bpmn文件

    部署流程,把画好的流程定义文件,加载到数据库中,生成表的数据

    1. @Slf4j
    2. @RunWith(SpringRunner.class)
    3. @SpringBootTest
    4. public class Demo {
    5. @Autowired
    6. private ProcessRuntime processRuntime;
    7. @Autowired
    8. private TaskRuntime taskRuntime;
    9. @Autowired
    10. private SecurityUtil securityUtil;
    11. /**
    12. * 查看流程定义内容
    13. */
    14. @Test
    15. public void findProcess(){
    16. //说明哪个用户正在执行这个方法
    17. securityUtil.logInAs("jack");
    18. //流程定义的分页对象
    19. Page definitionPage = processRuntime.processDefinitions(Pageable.of(0,10));
    20. log.info("流程定义总数:{}",definitionPage.getTotalItems());
    21. for (ProcessDefinition processDefinition : definitionPage.getContent()) {
    22. System.out.println("=====================================================");
    23. log.info("流程定义内容:{}",processDefinition);
    24. System.out.println("=====================================================");
    25. }
    26. }
    27. }

    在数据库中,就可以看的创建的表


    6.测试类-启动流程

    启动流程,使用java代码来操作数据库表中的内容

    1. /**
    2. * 启动流程
    3. */
    4. @Test
    5. public void startProcess(){
    6. //设置登录用户
    7. securityUtil.logInAs("system");
    8. ProcessInstance processInstance = processRuntime
    9. .start(ProcessPayloadBuilder.start().withProcessDefinitionKey("myProcess_1").build());
    10. log.info("流程实例内容:{}",processInstance);
    11. }

    控制台打印


    7.测试类-执行任务

    活动Activity  活动是工作或任务的一个通用术语。一个活动可以是一个任务,还可以是一个当前流程的子处理流程;其次,你还可以为活动指定不同的类型。

    1. /**
    2. * 执行任务
    3. */
    4. @Test
    5. public void doTask() {
    6. //设置登录用户 如果other用户属于别的组,查不到任务
    7. securityUtil.logInAs("rose");
    8. //查询任务
    9. Page taskPage = taskRuntime.tasks(Pageable.of(0, 10));
    10. if (taskPage != null && taskPage.getTotalItems() > 0) {
    11. for (Task task : taskPage.getContent()) {
    12. //拾取任务
    13. taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
    14. log.info("任务内容:{}", task);
    15. //完成任务
    16. taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
    17. }
    18. }
    19. }

    activiti的25张表

    1.表前缀含义

    ACT_RE:“RE”代表“Repository”(仓库),这些表中保存一些‘静态’信息,如流程定义和流程资源(如图片、规则等);
    ACT RU:'RU'表示runtime。这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据

    activiti只在流程实例执行过程中保存这些数据,在流程结束时就会别除这些记录。这样运行时表可以一直很小速度很快。

    ACT_HI* : “HI”代表“History”(历史),这些表中保存的都是历史数据,比如执行过的流程实例、变量、任务,等等。Activit默认提供了4种历史级别:

    Ø  none: 不保存任何历史记录,可以提高系统性能;

    Ø  activity:保存所有的流程实例、任务、活动信息;

    Ø  audit:也是Activiti的默认级别,保存所有的流程实例、任务、活动、表单属性;

    Ø  full:最完整的历史记录,除了包含audit级别的信息之外还能保存详细,例如:流程变量。

    对于几种级别根据对功能的要求选择,如果需要日后跟踪详细可以开启full

    ACT_GE:“GE”代表“General”(通用),用在各种情况下;


    2.activiti数据表清单

    此外还有两张表:ACT_EVT_LOG和ACT_PROCDEF_INFO没有按照规则来,两者分别属于HI和RE。


    3.获取service操作表(原生的)


    activiti的入门

    1.流程变量

    流程变量就是activiti在管理工作流时根据管理需要而设置的变量。在连线上使用UEL表达式,决定流程走向

    流程运转有时需要靠流程变量,比如:在出差申请流程流转时如果出差天数大于3天则由经理审核,否则侧由人事直接审核,出差天数就可以设置为流程变量,在流程流转时使用。

    流向Flow

    流是连接两个流程节点的连线。常见的流向包含以下几种:


    2.组任务

    a、查询组任务

    指定候选人,查询该候选人当前的待办任务。注意候选人不能立即办理任务。

    b、拾取(claim)任务

    该组任务的所有候选人都能拾取。
    将候选人的组任务,变成个人任务。原来候选人就变成了该任务的负责人。

    归还

    如果拾取后不想办理该任务?
    需要将已经拾取的个人任务归还到组里边,将个人任务变成了组任务。

    ====

    转办 

    c、查询个人任务

    查询方式同个人任务部分,根据assignee查询用户负责的个人任务。

    d、办理个人任务


    3.网关

    网关用来处理决策,有几种常用网关需要了解:

    为什么用网关
    如果不用网关,传过来的流程变量不符合任何一个条件,会马上结束当前的任务(这样不好)
    如果用的是排他网关,就不是结束任务,而是报错。(这样好)

    排他网关

    排他网关只会选择一个为true的分支执行。如果有两个分支条件都为true,排他网关会选择id值较小的一条分支去执行。

    并行网关

    并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的:
    fork分支:
    并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。
    join汇聚:
    所有到达并行网关,在此等待的进入分支,直到所有进入顺序流的分支都到达以后,流程就会通过汇聚网关。

    与其他网关的主要区别是,并行网关不会解析条件。即使顺序流中定义了条件,也会被忽略

    包含网关

    包含网关的功能是基于进入和外出顺序流的:
    分支:
    所有外出顺序流的条件都会被解析,结果为true的顺序流会以并行方式继续执行,会为每个顺序流创建一个分支。
    汇聚:
    所有并行分支到达包含网关,会进入等待状态,直到每个包含流程token的进入顺序流的分支都到达。这是与并行网关的最大不同。

    换句话说,包含网关只会等待被选中执行了的进入顺序流。在汇聚之后,流程会穿过包含网
    关继续执行。

  • 相关阅读:
    时间序列:时间序列模型---随机游走过程(The Random Walk Process)
    php文件操作
    Letcode-Top 100动态规划
    大数据之ZooKeeper(二)
    Java字节码学习笔记(一):Java字节码是什么?
    Leetcode 198. House Robber
    【ASP.NET Core】自定义Session的存储方式
    并查集(Union-Find)
    什么是模型监控?(Valohai)
    GBase 8a 负载均衡策略及JDBC连接方式
  • 原文地址:https://blog.csdn.net/m0_56799642/article/details/127846310