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文件全部内容如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.xym</groupId>
    <artifactId>activiti-project</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>7.0.0.Beta2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3.application.yml配置文件

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

4.SecurityUtil 和 DemoApplicationConfig 

/**
 * @Description:快速实现SpringSecurity安全框架的配置
 * @Author: xiaoyumao
 * @Date: 2022/11/25 16:21
 */
public class SecurityUtil {
    private Logger logger = LoggerFactory.getLogger(SecurityUtil.class);

    @Autowired
    @Qualifier("myUserDetailsService")
    private UserDetailsService userDetailsService;

    public void logInAs(String username) {
        UserDetails user = userDetailsService.loadUserByUsername(username);
        if (user == null) {
            throw new RuntimeException("用户" + username + "不存在");
        }
        logger.info(">Logged in as: " + username);
        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 b) throws IllegalArgumentException {

            }

            @Override
            public String getName() {
                return user.getUsername();
            }
        }));
        org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
    }
}
/**
 * @Description: 实现SpringSecurity框架的用户权限的配置
 * @Author: xiaoyumao
 * @Date: 2022/11/25 16:39
 */
@Slf4j
@Configuration
public class DemoApplicationConfig {
    private Logger logger = LoggerFactory.getLogger(DemoApplicationConfig.class);

    @Bean
    public UserDetailsService myUserDetailsService() {
        //把用户存储在内存中
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        //这里添加用户,后面处理流程用到的任务负责人在这里添加
        String[][] usersGroupsAndRoles = {
                {"jack", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"rose", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"tom", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"other", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"system", "password", "ROLE_ACTIVITI_USER"},
                {"admin", "password", "ROLE_ACTIVITI_USER"},
        };
        for (String[] user : usersGroupsAndRoles) {
            //把用户的角色和组拿出来
            List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
            logger.info(">Registering new user: " + user[0] + "with the following Authorities[" + authoritiesStrings + "]");
            inMemoryUserDetailsManager.createUser(new User(
                    user[0],
                    passwordEncoder().encode(user[1]),
                    authoritiesStrings.stream().map(str -> new SimpleGrantedAuthority(str)).collect(Collectors.toList())));
        }
        return inMemoryUserDetailsManager;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

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

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

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

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class Demo {

    @Autowired
    private ProcessRuntime processRuntime;

    @Autowired
    private TaskRuntime taskRuntime;

    @Autowired
    private SecurityUtil securityUtil;

    /**
     * 查看流程定义内容
     */
    @Test
    public void findProcess(){
        //说明哪个用户正在执行这个方法
        securityUtil.logInAs("jack");
        //流程定义的分页对象
        Page<ProcessDefinition> definitionPage = processRuntime.processDefinitions(Pageable.of(0,10));
        log.info("流程定义总数:{}",definitionPage.getTotalItems());
        for (ProcessDefinition processDefinition : definitionPage.getContent()) {
            System.out.println("=====================================================");
            log.info("流程定义内容:{}",processDefinition);
            System.out.println("=====================================================");
        }
    }
}

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


6.测试类-启动流程

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

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

控制台打印


7.测试类-执行任务

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

    /**
     * 执行任务
     */
    @Test
    public void doTask() {
        //设置登录用户 如果other用户属于别的组,查不到任务
        securityUtil.logInAs("rose");
        //查询任务
        Page<Task> taskPage = taskRuntime.tasks(Pageable.of(0, 10));
        if (taskPage != null && taskPage.getTotalItems() > 0) {
            for (Task task : taskPage.getContent()) {
                //拾取任务
                taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
                log.info("任务内容:{}", task);
                //完成任务
                taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
            }
        }
    }

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的进入顺序流的分支都到达。这是与并行网关的最大不同。

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值