工作流

工作流-Activiti

bpmn插件

Springboot 依赖

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter</artifactId>
    <version>7.1.0.M4</version>
</dependency>
<dependency>
    <groupId>org.activiti.dependencies</groupId>
    <artifactId>activiti-dependencies</artifactId>
    <version>7.1.0.M4</version>
    <type>pom</type>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

配置数据库连接

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db_activiti?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=xxxx

# spring boot 整合activiti默认关闭历史表,手动开启历史表如下:
spring.activiti.history-level=audit
spring.activiti.db-history-used=true

# 可选
#流程定义bpmn放置路径
spring.activiti.process-definition-location-prefix=classpath:/process/
#项目随着spring启动自动部署
spring.activiti.check-process-definitions=true

改正上述activiti版本创建数据表的bug

启动应用创建表后,运行:

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- 创建用户表
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL COMMENT '姓名',
  `address` varchar(64) DEFAULT NULL COMMENT '联系地址',
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',
  `roles` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '角色',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- 填充用户表
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'admincn', 'beijing', 'admin', '$2a$10$gw46pmsOVYO.smHYQ2jH.OoXoe.lGP8OStDkHNs/E74GqZDL5K7ki', 'ROLE_ACTIVITI_ADMIN');
INSERT INTO `user` VALUES ('2', 'bajiecn', 'shanghang', 'bajie', '$2a$10$gw46pmsOVYO.smHYQ2jH.OoXoe.lGP8OStDkHNs/E74GqZDL5K7ki', 'ROLE_ACTIVITI_USER,GROUP_activitiTeam,g_bajiewukong');
INSERT INTO `user` VALUES ('3', 'wukongcn', 'beijing', 'wukong', '$2a$10$gw46pmsOVYO.smHYQ2jH.OoXoe.lGP8OStDkHNs/E74GqZDL5K7ki', 'ROLE_ACTIVITI_USER,GROUP_activitiTeam');
INSERT INTO `user` VALUES ('4', 'salaboycn', 'beijing', 'salaboy', '$2a$10$gw46pmsOVYO.smHYQ2jH.OoXoe.lGP8OStDkHNs/E74GqZDL5K7ki', 'ROLE_ACTIVITI_USER,GROUP_activitiTeam');

-- ----------------------------
-- 修复Activiti7的M4版本缺失字段Bug
-- ----------------------------
alter table ACT_RE_DEPLOYMENT add column PROJECT_RELEASE_VERSION_ varchar(255) DEFAULT NULL;
alter table ACT_RE_DEPLOYMENT add column VERSION_ varchar(255) DEFAULT NULL;

-- ----------------------------
-- 动态表单数据存储
-- ----------------------------
DROP TABLE IF EXISTS `formdata`;
CREATE TABLE `formdata` (
  `PROC_DEF_ID_` varchar(64) DEFAULT NULL,
  `PROC_INST_ID_` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `FORM_KEY_` varchar(255) DEFAULT NULL,
  `Control_ID_` varchar(100) DEFAULT NULL,
  `Control_VALUE_` varchar(2000) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

spring security

spring-activiti自动集成了spring boot security,访问应用的用户名是:user,启动时会在控制台生成密码:

2020-12-26 20:59:29.582  INFO 11832 --- [       main] .s.s.UserDetailsServiceAutoConfiguration :

Using generated security password: b241ae9b-ba60-44a9-8c0d-b5ca45349ed6

去掉密码:在启动类前加注解:

@SpringBootApplication(exclude = &#123;org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class,
        org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration.class&#125;)
public class ActivitiSpringbootDemoApplication &#123;

    public static void main(String[] args) &#123;
        SpringApplication.run(ActivitiSpringbootDemoApplication.class, args);
    &#125;

&#125;

部署和执行

部署的bpmn文件一定要是bpmn格式的文件,不能是xml格式的

部署流程会影响的表:act_re_deployment、act_re_procdef、act_ge_bytearray,如果其中任意一个表中没有写入,则没有部署成功。

@Service
public class EnterpriseRaService &#123;
    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    // 部署流程:
    public String deploy()&#123;
        Deployment deployment=repositoryService.createDeployment()
                .addClasspathResource("processes/analyse1.bpmn")
                .name("请假流程")
                .deploy();
        return deployment.getId();
    &#125;

    // 执行流程
    public String startProcess()&#123;
        ProcessInstance instance=runtimeService.startProcessInstanceByKey("process_analyse");
        return instance.getId();
    &#125;
&#125;

Service Task

参考:自动服务任务
服务任务
service task需要在bpmn文件中配置与之关联的执行类,类名要写全名,即package.类名,async设置为true表示startProcessInstanceByKey方法立即返回,然后异步执行service task;

 <bpmn2:serviceTask id="Activity_0z1g1ix" name="crawl" activiti:async="true" activiti:class="com.example.cloudactiviti.listener.CrawlListener">

与之关联的类实现JavaDelegate接口:

package com.example.cloudactiviti.listener;

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;

@Component
public class CrawlListener implements JavaDelegate &#123;
    @Component
    public class CrawlListener implements JavaDelegate &#123;

        @Override
        public void execute(DelegateExecution delegateExecution) &#123;
            System.out.println("----------------------");
            System.out.println("-----模拟事务-------");
            try &#123;
                for (int i = 0; i < 10; i++) &#123;
                    System.out.println(i);
                    Thread.sleep(1000);
                &#125;
            &#125; catch (InterruptedException e) &#123;
                e.printStackTrace();
            &#125;
            System.out.println("end");
        &#125;
    &#125;
&#125;

然后调用startProcessInstanceByKey执行流程,该方法立即返回,流程异步执行。

查询当前流程实例已完成任务

其实是查询act_hi_actinst表,按照时间排序,返回最后完成的活动。

public String getLastFinishedTask(String instanceId)&#123;
    List<HistoricActivityInstance> list=historyService.createHistoricActivityInstanceQuery()
            .processInstanceId(instanceId).finished().orderByHistoricActivityInstanceStartTime().desc().list();
    String lastFinishedTask = "空任务";
    if(list!=null && list.size()>0)&#123;
        lastFinishedTask=list.get(0).getActivityName();
        for(HistoricActivityInstance taskInstance:list)&#123;
            System.out.println("已完成:"+taskInstance.getActivityName());
        &#125;
    &#125;else&#123;
        System.out.println("---------------当前任务为空-------------------");
    &#125;
    return lastFinishedTask;
&#125;

查询正在执行的任务节点id

public String getCurrentTask(String instanceId)&#123;
    List<Execution> executionList=runtimeService.createExecutionQuery().processInstanceId(instanceId)
            .list();
    String currentTask="已结束";
    if(executionList==null || executionList.size()<1)&#123;
        return currentTask;
    &#125;else&#123;
        Execution execution=executionList.get(1);
        // 获取正在执行的活动id
        currentTask=execution.getActivityId();
        if(currentTask.equals("Activity_0z1g1ix"))&#123;
            currentTask="crawl";
        &#125;else if(currentTask.equals("Activity_1rgp0z1"))&#123;
            currentTask="analyse";
        &#125;
    &#125;
    return currentTask;
&#125;

参数的设置和获取

在启动Activiti流程实例的时候,设置参数字典:

public String startProcess(String a,String b)&#123;

    Map<String,Object> mapVariables = new HashMap<>();
    mapVariables.put("strA",a);
    mapVariables.put("strB",b);

    ProcessInstance instance=
            runtimeService.startProcessInstanceByKey("process_analyse",mapVariables);

    return instance.getId();
&#125;

然后就可以在JavaDelegate类中获取参数:

@Component
public class CrawlListener implements JavaDelegate &#123;

    public void execute(DelegateExecution delegateExecution) &#123;
        String taskId = delegateExecution.getVariable("strA",String.class);
        String userId = delegateExecution.getVariable("strB",String.class);

    &#125;
&#125;

监听类中注入bean

实现JavaDelegate的service task监听类中,采用Autowired注入bean为null,需要BeanFactoryPostProcessor获取。

import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

/**
 * spring工具类 方便在非spring管理环境中获取bean
 *
 * @author aaa
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor
&#123;
    /** Spring应用上下文环境 */
    private static ConfigurableListableBeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
    &#123;
        SpringUtils.beanFactory = beanFactory;
    &#125;

    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws org.springframework.beans.BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException
    &#123;
        return (T) beanFactory.getBean(name);
    &#125;

    /**
     * 获取类型为requiredType的对象
     *
     * @param clz
     * @return
     * @throws org.springframework.beans.BeansException
     *
     */
    public static <T> T getBean(Class<T> clz) throws BeansException
    &#123;
        T result = (T) beanFactory.getBean(clz);
        return result;
    &#125;

    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name)
    &#123;
        return beanFactory.containsBean(name);
    &#125;

    /**
     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
    &#123;
        return beanFactory.isSingleton(name);
    &#125;

    /**
     * @param name
     * @return Class 注册对象的类型
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
    &#123;
        return beanFactory.getType(name);
    &#125;

    /**
     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     * @return
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
    &#123;
        return beanFactory.getAliases(name);
    &#125;

    /**
     * 获取aop代理对象
     *
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker)
    &#123;
        return (T) AopContext.currentProxy();
    &#125;
&#125;

然后用SpringUtils.getBean(注入类.class)获取。

IActivitiGraph activitiGraph=SpringUtils.getBean(IActivitiGraph.class);
代替
@Autowired
IActivitiGraph activitiGraph;

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 changzeyan@foxmail.com

×

喜欢就点赞,疼爱就打赏