JavaEE:Activiti工作流使用

说明:
Activiti是工作流引擎,用于请假等各种流程实现自动化控制,和Jbpm有点类似。

自动生成的数据库act_*表说明:
act_ge_*:包含通用数据。
act_hi_*:包含历史数据,历史流程实例、变量、任务等。
act_re_*:包含流程定义和静态资源。
act_ru_*:包含流程实例、任务、变量、异步任务等运行时数据,流程结束时会删除这些表的数据。

SpringBoot配置:https://blog.csdn.net/a526001650a/article/details/106687559

一、IDEA安装actiBPM插件:
1.IntelliJ IDEA安装actiBPM插件,从以下地址下载插件,从本地选择安装:
https://plugins.jetbrains.com/plugin/7429-actibpm/versions

2.解决流程节点中文名称乱码,IDEA\bin目录下,在idea.exe.vmoptions和idea64.exe.vmoptions末尾增加:
-Dfile.encoding=UTF-8

二、流程文件创建:

1.创建流程文件:

(1)新建一个流程文件,右击工程resources/processes目录->New->BpmnFile->New BpmnFile对话框中输入文件名leave,点OK创建:

(2)双击打开leave.bpmn文件,将右边流程节点拖入中间空白处,设计流程图(中间空白处为流程设计处,右侧为流程节点):

StartEvent:流程开始
UserTask:任务节点
EndEvent:流程结束

(3)面板图标说明:

StartEvent:任务开始,必须有
UserTask:每个任务节点,必须有
EndEvent:任务结束,必须有
ExclusiveGateway:排他网关
ParallelGateway:并行网关,会忽略流程线中设置的条件
InclusiveGateway:包含网关,排他网关和并行网关结合体

(4)拖动一个StartEvent和EndEvent作为开始与结束,拖动6个UserTask作为任务节点,光标放在节点中心处按住拖出线,将每个节点连接起来:

(5)点击空白处,在左侧设置流程图的Id和Name值:

(6)依次选中每个任务节点,在左侧Assignee栏输入操作人:

(7)将bpmn转换为png格式,bpmn重命名为xml,打开导出为png格式。

2..网关:

(1)不使用网关:

多条线同时满足条件时,多条线同时执行;
多条线同时不满足条件时,抛出ActivitiException异常。

(2)排他网关ExclusiveGateway:

多条线同时满足时,选id值最小的执行;
多条线同时不满足条件时,抛出ActivitiException异常。

打开leave.bpmn文件,右侧控件面板,拖出ExclusiveGateway,放在总监审批节点后面,后面2条线都从ExclusiveGateway延伸,如图:

(3)并行网关ParallelGateway(会忽略流程线中设置的条件):

1>第1个ParallelGateway节点,将流程线从一条线拆分成多条线;
2>执行并行节点的任务,如人事存档和财务存档;
3>第2个ParallelGateway节点,将流程线从多条线合并成一条线,待多条线汇聚完成后,才会执行下一结节。

打开leave.bpmn文件,右侧控件面板,拖出第1个ParallelGateway,放在总监节点后面 -> 将ParallelGateway拖出2条线接到人事存档和财务存档任务节点 -> 拖出第2个ParallelGateway,将人事存档和财务存档流程线合并到此ParallelGateway,如图:

(4)包含网关InclusiveGateway(排他网关和并行网关结合体):

1>第1个InclusiveGateway节点,将流程线从一条线拆分成多条线,多条线可以设条件,只有满足条件的线才会执行;
2>执行并行节点的任务,如人事存档或财务存档;
3>第2个InclusiveGateway节点,将流程线从满足条件的多条线合并成一条线,待多条线汇聚完成后,才会执行下一结节。

打开leave.bpmn文件,右侧控件面板,拖出第1个InclusiveGateway,放在总监节点后面 -> 将InclusiveGateway拖出2条线接到人事存档和财务存档任务节点,对2条件设置不同条件 -> 拖出第2个InclusiveGateway,将人事存档和财务存档流程线合并到此InclusiveGateway,如图:

三、配置使用流程的工程:

1.加入activiti依赖和mysql驱动依赖,在工程/pom中:

<dependencies>
    <!-- 导入activiti依赖包 -->
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-spring-boot-starter</artifactId>
        <version>7.1.0.M6</version>
    </dependency>
    <!-- 导入spring-security依赖包 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
        <version>2.3.0.RELEASE</version>
    </dependency>
    <!-- 导入mysql驱动包 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.20</version>
    </dependency>
    <!-- 必须导入jdbc依赖包,解决找不到database的问题 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
</dependencies>

<!-- 添加私服仓库地址 -->
<repositories>
    <repository>
        <id>alfresco</id>
        <name>Activiti Releases</name>
        <url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases/</url>
        <releases>
            <enabled>true</enabled>
        </releases>
    </repository>
</repositories>

2.配置流程文件路径和mysql数据库连接,在resources/application.yml中:

server:
  port: 10000

spring:
  datasource: #mysql数据库连接信息
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC
    username: root
    password: root
  activiti: #activiti配置
    database-schema-update: true  #是否生成表结构
    #process-definition-location-prefix: "classpath*:/processes/*.bpmn"  #配置bpmn流程路径
    history-level: full #full表示使用全部记录历史
    db-history-used: true  #配置生成history表,默认false,只会生成17张表

3.配置SecurityAutoConfiguration,在启动类Application中:

@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class MyApplication {
    public static void main(String[] args) {//应用入口
        SpringApplication.run(MyApplication.class, args);  //运行MyApplication
    }
}

4.相关的类创建(找个使用的地方定义):

(1)传统方式:

//流程的资源管理
@Autowired
private RepositoryService repositoryService;
//流程的运行管理
@Autowired
private RuntimeService runtimeService;
//流程的历史管理
@Autowired
private HistoryService historyService;
//流程的任务管理
@Autowired
private TaskService taskService;

(2)Activiti7新API方式:

//新API方式:流程的资源管理
@Autowired
private ProcessRuntime processRuntime;
//此SecurityUti为自定义类
@Autowired
SecurityUtil securityUtil;
@Autowired //新API方式:任务管理
private TaskRuntime taskRuntime;

四、流程-部署/启动:

1.部署:

//1.1.流程-部署,此微服务启动时就执行
public void deploy() {
//  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
//  repositoryService = engine.getRepositoryService();
    repositoryService.createDeployment()
                .addClasspathResource("leave.bpmn")
                .addClasspathResource("leave.png")
                .name("请假流程")
                .deploy();
}

//1.2.流程-部署,此微服务启动时就执行,Zip包方式
public void deployZip() {
    ZipInputStream zin = new ZipInputStream(getClass().getClassLoader().getResourceAsStream("leave.zip")); //leave.zip包含了leave.bpmn和leave.png
    repositoryService.createDeployment()
                .addZipInputStream(zin)
                .name("请假流程")
                .deploy();
}

2.启动:

(1)传统方式:

//2.流程-启动,页面上点提交请假流程时执行
public void start() {
    //1.1 启动流程实例,leave为整个流程图上设置的id值
    //runtimeService.startProcessInstanceByKey("leave");
    //1.2 启动流程实例,第2个参数为业务标识businessKey,存在act_ru_execution表BUSINESS_KEY_列
    //ProcessInstance pi = runtimeService.startProcessInstanceByKey("leave", "businessKey");
    //String bkey = pi.getBusinessKey(); //获取businessKey
    //1.3 启动流程实例,第2个参数map为流程变量,控制流程走向
    Map<String, Object> map = new HashMap<>();
    map.put("day", 3);  //传入day值,控制流程走向
    ProcessInstance pi = runtimeService.startProcessInstanceByKey("leave", map);
    //通过流程实例ID传入参数
    //runtimeService.setVariables("流程实例ID", map);
}

(2)Activiti7新API方式:

//启动流程实例
public void startBy7() {
    securityUtil.logInAs("yyh0"); //认证用户信息,用户名要和权限组中的一样
    processRuntime.start(ProcessPayloadBuilder
                .start()
                .withProcessDefinitionKey("leave")  //设置用哪张流程图,leave为流程图中设置的id
                .withName("请假流程")  //流程名称
//                .withVariable("day", 3)  //传入流程变量
//                .withBusinessKey("businessKey")
                .build());
}

五、流程定义相关的操作(leave为请假流程设置的id值):

1.查询流程信息:

(1)传统方式:

public void queryProcess() {
    List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()
        .processDefinitionKey("leave")   //leave为请假流程设置的id值
        .orderByProcessDefinitionVersion().desc() //对结果按版本号降序
        .list();
    for (ProcessDefinition item : list) {
        item.getId(); //流程id
        item.getName(); //流程名称
        item.getKey(); //流程key
        item.getVersion(); //流程版本
    }
}

(2)Activiti7新API方式:

public void queryProcessBy7() {
    securityUtil.logInAs("yyh0"); //认证用户信息,用户名要和权限组中的一样
    Page page = processRuntime.processDefinitions(Pageable.of(0, 20));
    List<ProcessDefinition> list = page.getContent();
    for (ProcessDefinition item : list) {
        item.getId(); //流程id
        item.getName(); //流程名称
        item.getKey(); //流程key
        item.getVersion(); //流程版本
    }
}

2.删除流程:

public void deleteProcess(String deploymentId) {  //deployment通过queryProcess查出来
    repositoryService.deleteDeployment(deploymentId, true);  //设为true时即使启动了也能删除。反之不行
}

3.获取流程文件名与文件流:

public void queryProcessFile() {
    ProcessDefinition item = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave").singleResult();
    //获取流程文件名称
    String bpmnName = item.getResourceName();
    String pngName = item.getDiagramResourceName();
    //获取流程文件的流
    InputStream bpmnIn = repositoryService.getResourceAsStream(item.getDeploymentId(), bpmnName);
    InputStream pngIn = repositoryService.getResourceAsStream(item.getDeploymentId(), pngName);
}

六、流程实例相关的操作(leave为请假流程设置的id值):

1.暂停XX流程的所有实例:

public void pauseAllInstance() {
    ProcessDefinition item = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave").singleResult();
    if (!item.isSuspended()) { //流程实例没有暂停
        repositoryService.suspendProcessDefinitionById(item.getId(), true, null);  //暂停此流程下的所有流程实例
    }
}

2.激活XX流程的所有实例:

public void resumeAllInstance() {
    ProcessDefinition item = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave").singleResult();
    if (item.isSuspended()) { //流程实例已经暂停
        repositoryService.activateProcessDefinitionById(item.getId(), true, null);  //激活此流程下的所有流程实例
    }
}

3.暂停XX流程的某个实例:

public void pauseInstance(String instanceId) {//instanceId为流程实例id
    ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
    if (!pi.isSuspended()) { //流程实例没有暂停
        runtimeService.suspendProcessInstanceById(pi.getId());  //暂停单个流程实例
    }
}

4.激活XX流程的某个实例:

public void resumeInstance(String instanceId) {
    ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId("leave").singleResult();
    if (pi.isSuspended()) { //流程实例已经暂停
        runtimeService.activateProcessInstanceById(pi.getId());  //激活单个流程实例
    }
}

5.查询XX流程实例的历史信息:

public void queryHistory() {
    List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId("leave")
                .orderByHistoricActivityInstanceStartTime().desc() //排序
                .list();
    for (HistoricActivityInstance item : list) {//获取每一个流程信息
        item.getActivityId(); //流程-节点id,ACT_ID_
        item.getActivityName(); //流程-节点名称,ACT_NAME_
        item.getAssignee(); //流程-任务节点执行人,ASSIGNEE_
        item.getProcessDefinitionId(); //流程id,例:1.4
        item.getProcessInstanceId(); //流程实例id,EXECUTION_ID_
    }
}

七、流程任务相关的操作:

1.查询名下的个人任务:

(1)传统方式:

public Task findTask(String username) {
    Task task = taskService.createTaskQuery()
                .processDefinitionKey("leave") //leave为整个流程id值
                .taskAssignee(username) //查个人任务
                .singleResult();
    //通过任务ID传入参数
//  Map<String, Object> map = new HashMap<>();
//  map.put("day", 3);  //传入day值,控制流程走向
//  taskService.setVariables("任务ID", map);
//  taskService.setVariablesLocal("任务ID", map);  //local作用域,只对当前任务节点有效果
    return task;
}

(2)Activiti7新API方式:

public List<Task> findTaskBy7(String username) {
    securityUtil.logInAs("yyh0"); //认证用户信息,用户名要和权限组中的一样
    return taskRuntime.tasks(Pageable.of(0, 20)).getContent();
}

2.查询名下的组任务:

(1)设置组任务(设置多个候选人):

打开bpmn流程图 -> 需要候选人的任务节点 -> 在左侧Candidate Users栏,输入多个角色以逗号隔开,如图:

(2)查询:

public Task findGroupTask(String username) {
    return taskService.createTaskQuery()
                .processDefinitionKey("leave") //leave为整个流程id值
                .taskCandidateUser(username) //候选人查组任务
                .singleResult();
}

3.拾取组任务为个人任务:

(1)传统方式:

public void claim(String username) {
    Task task = findGroupTask(username);
    if (task == null) {
        return;
    }
    taskService.claim(task.getId(), username);
}

(2)Activiti7新API方式:

public void claimBy7(String username) {
    securityUtil.logInAs("yyh0"); //认证用户信息,用户名要和权限组中的一样
    List<Task> tasks = findTaskBy7(username);
    if (tasks == null || tasks.isEmpty()) {
        return;
    }
    for (Task task : tasks) {
        taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
    }
}

4.取消拾取,重置为组任务:

public void cancelClaim(String username) {
    Task task = findTask(username);
    if (task == null) {
        return;
    }
    taskService.setAssignee(task.getId(), null);  //执行人设为null,则重新变为组任务
}

5.修改任务处理人:

public void changeAssignee(String oldName, String newName) {
    Task task = findTask(oldName);
    if (task == null) {
        return;
    }
    taskService.setAssignee(task.getId(), newName);  //执行人设为新用户
}

6.处理个人任务:

(1)传统方式:

public void complete(String username) {
    Task task = findTask(username);
    if (task == null) {
        return;
    }
    //1.1 完成任务
    //taskService.complete(task.getId());
    //1.2 完成任务,第2个参数map为流程变量,控制流程走向
    Map<String, Object> map = new HashMap<>();
    map.put("day", 4);  //传入day值,控制流程走向
    taskService.complete(task.getId(), map);
}

(2)Activiti7新API方式:

public void completeBy7(String username) {
    securityUtil.logInAs("yyh0"); //认证用户信息,用户名要和权限组中的一样
    List<Task> tasks = findTaskBy7(username);
    if (tasks == null || tasks.isEmpty()) {
        return;
    }
    for (Task task : tasks) {
        Map<String, Object> map = new HashMap<>();
        map.put("day", 4);  //传入day值,控制流程走向
        taskRuntime.complete(TaskPayloadBuilder.complete().withVariables(map).withTaskId(task.getId()).build());
    }
}

八、设置处理任务的角色:

1.固定方式,打开bpmn流程图,点击每个任务节点,在左侧Assignee栏输入固定的用户名。

2.UEL表达式方式:

说明:打开bpmn流程图 -> 点击每个任务节点左侧Assignee栏,将写死的用户名替换为UEL表达式。

(1)UEL-value表达式:

定义流程变量:
格式:${自定义键名}
例:${key1}

流程实例启动时传入参数:

Map<String, Object> map = new HashMap<>();
map.put("key1", "yyh0");  //将bpmn流程图左侧Assignee栏的${key1},设值为yyh1
runtimeService.startProcessInstanceByKey("leave", map);

(2)UEL-method表达式:

格式:${javaBean对象名.变量名},此javaBean对象名必须是spring容器中的一个bean,实现Serializable,生成serialVersionUID。
例:${user.name}

流程实例启动时传入参数:

Map<String, Object> map = new HashMap<>();
User user = new User();
user.setName("yyh0");
map.put("user", user);  //将bpmn流程图左侧Assignee栏的${user.name},设值为yyh0
runtimeService.startProcessInstanceByKey("leave", map);

3.监听器方式:

(1)实现TaskListener接口,设置Assignee栏的值:

public class TaskLinstener1 implements org.activiti.engine.delegate.TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) {
        delegateTask.setAssignee("yyh0"); //设置Assignee栏的值
    }
}

(2)打开bpmn流程图,点击每个任务节点,在左侧Task Listeners -> 新增一个Listener:

Event=Create
Type=Class
Class=选择TaskLinstener1类文件

九、增加条件控制流程走向:

globa变量:作用域为整个流程实例
local变量:作用域为当前任务节点或者当前连接线

1.打开bpmn流程图 -> 选中2个任务节点之间的线 -> 在左侧Condition栏,输入UEL表达式控制流程走向,如图:

(1)选中指向总经理的线,输入:

${day>3}

(2)选中指向结束的线,输入:

${day<=3}

2.传入参数值,赋值给上一步的day,控制流程走向:

(1)启动流程实例时传入参数:

Map<String, Object> map = new HashMap<>();
map.put("day", 3);
runtimeService.startProcessInstanceByKey("leave", map);

(2)在任务完成时传入参数:

taskService.complete(taskId, map);

(3)通过流程实例ID传入参数(流程实例未结束为前提):

runtimeService.setVariables(流程实例ID, map);

(4)通过当前节点的任务ID传入参数(当前节点任务未结束为前提):

taskService.setVariables("任务ID", map);
taskService.setVariablesLocal("任务ID", map); //local作用域

十、SecurityUtil和MySecurityConfig配置类:

1.SecurityUtil(用于认证登录):

官方例子:

https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-process-example/src/main/java/org/activiti/examples/SecurityUtil.java

代码:

@Component
public class SecurityUtil { //此类直接下载
    @Autowired
    private UserDetailsService userDetailsService;
    public void logInAs(String username) {
        UserDetails details = userDetailsService.loadUserByUsername(username);
        if (details == null) {
            return;
        }
        SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
            @Override
            public Collection<? extends GrantedAuthority> getAuthorities() {
                return details.getAuthorities();
            }
            @Override
            public Object getCredentials() {
                return details.getPassword();
            }
            @Override
            public Object getDetails() {
                return details;
            }
            @Override
            public Object getPrincipal() {
                return details;
            }
            @Override
            public boolean isAuthenticated() {
                return true;
            }
            @Override
            public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
            }
            @Override
            public String getName() {
                return details.getUsername();
            }
        }));
        org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
    }
}

2.MySecurityConfig(配置权限组):

官方例子:

https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-process-example/src/main/java/org/activiti/examples/DemoApplicationConfiguration.java

代码:

@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    @Autowired
    public void configure(AuthenticationManagerBuilder am) throws Exception {
        am.userDetailsService(userDetailsService());
    }
    @Override
    protected void configure(HttpSecurity hs) throws Exception {
        hs.csrf().disable().authorizeRequests()
                .anyRequest().authenticated().and().httpBasic();
    }
    @Bean
    public UserDetailsService userDetailsService() {//自定义方法
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        //权限组,用于认证登录
        String[][] roles = {
                {"yyh0", "123456", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"yyh1", "123456", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"yyh2", "123456", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"yyh3", "123456", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"yyh4", "123456", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"yyh5", "123456", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"other", "123456", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"},
                {"admin", "123456", "ROLE_ACTIVITI_ADMIN"},
        };
        for (String[] role : roles) {
            List<String> list = Arrays.asList(Arrays.copyOfRange(role, 2, role.length));
            manager.createUser(new User(role[0], passwordEncoder().encode(role[1]),
                    list.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList())));
        }
        return manager;
    }
    @Bean
    public PasswordEncoder passwordEncoder() {//自定义方法
        return new BCryptPasswordEncoder();
    }
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值