一文解读flowable工作流-程序员宅基地

技术标签: java  后端  学习总结  

flowable工作流的定义 ,流程 及项目使用


1. flowable

️ 1.1 定义

  Flowable是一个开源的业务流程管理(BPM)平台,它可以帮助开发人员和业务专家快速构建、部署和管理复杂的业务流程。Flowable提供了一个可视化的工作流设计器,可以方便地定义工作流程、任务、网关、决策等元素,并支持多种流程引擎的选择,如Activiti、Camunda等。

Flowable的核心特性包括:

  1. 可视化工作流设计器:Flowable提供了一个直观、易用的工作流设计器,可以方便地定义工作流程、任务、网关、决策等元素。
  2. 多引擎支持:Flowable支持多种流程引擎的选择,如Activiti、Camunda等,可以根据应用程序的需求进行选择。
  3. 扩展性:Flowable提供了丰富的API和插件,可以方便地进行定制和扩展。
  4. 集成性:Flowable可以与Spring、Hibernate等常见的Java框架集成,从而实现更好的互操作性和可维护性。

️ 1.2 作用

  Flowable可以十分灵活地加入你的应用/服务/构架。可以将JAR形式发布的Flowable库加入应用或服务,来嵌入引擎。
  以JAR形式发布使Flowable可以轻易加入任何Java环境:Java SE;Tomcat、Jetty或Spring之类的servlet容器;JBoss或WebSphere之类的Java EE服务器,等等。
  另外,也可以使用Flowable REST API进行HTTP调用。
  也有许多Flowable应用(Flowable Modeler, Flowable Admin, Flowable IDM 与 Flowable Task),提供了直接可用的UI示例,可以使用流程与任务。
  所有使用Flowable方法的共同点是核心引擎。核心引擎是一组服务的集合,并提供管理与执行业务流程的API。

️ 1.3 流程

Flowable的流程通常由以下几个步骤组成:

  1. 定义工作流:使用Flowable的工作流设计器定义工作流,包括流程定义、任务、网关、决策等元素。
  2. 配置流程引擎:选择合适的流程引擎,如Activiti、Camunda等,并进行相应的配置和集成。
  3. 创建流程实例:根据工作流定义创建一个新的流程实例,并将任务分配给相应的人员或组。
  4. 执行流程:流程实例启动后,会按照预定义的流程进行执行,包括任务完成、网关判断、决策结果等。
  5. 监控流程:可以使用Flowable提供的监控工具对流程进行实时监控和管理,包括查看流程状态、任务完成情况、性能指标等。

  总之,Flowable的流程通常由工作流定义、流程引擎配置、流程实例创建和执行、流程监控等多个步骤组成,可以帮助快速构建、部署和管理复杂的业务流程。


2. 使用实例

2.1 引入pom依赖

  Flowable引擎在运行流程实例时,需要使用数据库来存储执行与历史数据,所以需要添加对应的数据库驱动依赖。
Flowable使用SLF4J作为内部日志框架。

		<!--flowable 流程引擎-->
		<!-- https://mvnrepository.com/artifact/org.flowable/flowable-spring-boot-starter-process --> 
		
		<dependency>
			<groupId>org.flowable</groupId>
			<artifactId>flowable-spring-boot-starter-process</artifactId>
			<version>6.4.1</version>
		</dependency>
		<dependency>
			<groupId>org.flowable</groupId>
			<artifactId>flowable-ui-modeler-rest</artifactId>
			<version>6.4.1</version>
		</dependency>
		
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

		<dependency>
		  <groupId>org.slf4j</groupId>
		  <artifactId>slf4j-api</artifactId>
		  <version>1.7.21</version>
		</dependency>
		<dependency>
		  <groupId>org.slf4j</groupId>
		  <artifactId>slf4j-log4j12</artifactId>
		  <version>1.7.21</version>
		</dependency>

Log4j需要一个配置文件。在src/main/resources文件夹下添加log4j.properties文件,并写入下列内容:

log4j.rootLogger=DEBUG, CA

log4j.appender.CA=org.apache.log4j.ConsoleAppender
log4j.appender.CA.layout=org.apache.log4j.PatternLayout
log4j.appender.CA.layout.ConversionPattern= %d{
    hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n

2.2 yml文件

数据库自行创建
一个简单的yml配置:

spring:
  #
  datasource:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/flowable_gremola?useUnicode=true&zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
      username: root
      password: root
  application:
    name: flowable-server
server:
  servlet:
    context-path: /scn_flowable
  port: 9602

#flowable
flowable:
  common:
    app:
      idm-url: http://127.0.0.1:9602/flowable-idm
      async-executor-activate: false
****************************************************************      
上面是项目使用的flowable的部分yml代码 通用参考下文 数据库自行创建
spring:
  datasource:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/flowable_gremola?useUnicode=true&zeroDateTimeBehavior=convertToNull&characterEncoding=UTF8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
      username: root
      password: root
      resources:
      # 
flowable:
 #定时任务关闭
 async-executor-activate: false
 server:
  port: 9602

2.3 审批流程xml

一个简单的审批流程的demo:
将下面的XML保存在src/main/resources文件夹下

<?xml version='1.0' encoding='UTF-8'?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef">
    <process id="SG_CTO_JL_CLOW" name="测试审批流程" isExecutable="true">
        <documentation>测试审批流程</documentation>
        <startEvent id="startEvent1" name="开始"/>
        <userTask id="sid-0CFA58AE-301B-4FDE-8329-80E528792596" name="测试审批" flowable:candidateGroups="${JLApprovePsn}"/>
        <sequenceFlow id="sid-D13A2CBF-4B62-463F-8552-76BC0DCEB04D" sourceRef="startEvent1" targetRef="sid-0CFA58AE-301B-4FDE-8329-80E528792596"/>
        <serviceTask id="sid-9C8B4BD9-CDFF-4779-8C2F-A3409E285E6B" name="通过" flowable:class="cn.sap.flowable.pbaseProccessFlow.serverTask.SGTJL.JLServiceFlowAgreeTaskClass"/>
        <serviceTask id="sid-80A8FCA9-953C-4A3B-B242-8F4E4677142E" name="不通过" flowable:class="cn.sap.flowable.pbaseProccessFlow.serverTask.SGTJL.JLServiceFlowDisAgreeTaskClass"/>
        <endEvent id="sid-9E4D02B7-8A60-4E6A-AFAD-A976EC2111B3" name="结束"/>
        <sequenceFlow id="sid-6315F048-1E32-4528-8438-3A9F8F0FE892" sourceRef="sid-9C8B4BD9-CDFF-4779-8C2F-A3409E285E6B" targetRef="sid-9E4D02B7-8A60-4E6A-AFAD-A976EC2111B3"/>
        <endEvent id="sid-2EA8D975-04C8-4BC8-9E9B-779589ADFA7F" name="结束"/>
        <sequenceFlow id="sid-77140DB2-331F-458C-B0B9-C5C4EFAB73E7" sourceRef="sid-80A8FCA9-953C-4A3B-B242-8F4E4677142E" targetRef="sid-2EA8D975-04C8-4BC8-9E9B-779589ADFA7F"/>
        <exclusiveGateway id="sid-FDBA37A5-9EAA-4BB0-95BF-086EC83B8CDC"/>
        <sequenceFlow id="sid-9C5BA16C-5042-477F-B4CD-7476722A96FA" sourceRef="sid-0CFA58AE-301B-4FDE-8329-80E528792596" targetRef="sid-FDBA37A5-9EAA-4BB0-95BF-086EC83B8CDC"/>
        <sequenceFlow id="sid-8BD19FD1-6D24-4C77-9B8C-F6C84D94F0ED" name="同意" sourceRef="sid-FDBA37A5-9EAA-4BB0-95BF-086EC83B8CDC" targetRef="sid-9C8B4BD9-CDFF-4779-8C2F-A3409E285E6B">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${
    agree=="agree"}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="sid-7F7721CC-E147-4FA3-B3F3-474D1D31CD98" name="不同意" sourceRef="sid-FDBA37A5-9EAA-4BB0-95BF-086EC83B8CDC" targetRef="sid-80A8FCA9-953C-4A3B-B242-8F4E4677142E">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${
    agree=="disagree"}]]></conditionExpression>
        </sequenceFlow>
    </process>
    <bpmndi:BPMNDiagram id="BPMNDiagram_SG_CTO_JL_CLOW">
        <bpmndi:BPMNPlane bpmnElement="SG_CTO_JL_CLOW" id="BPMNPlane_SG_CTO_JL_CLOW">
            <bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
                <omgdc:Bounds height="30.0" width="30.0" x="60.0" y="185.0"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-0CFA58AE-301B-4FDE-8329-80E528792596" id="BPMNShape_sid-0CFA58AE-301B-4FDE-8329-80E528792596">
                <omgdc:Bounds height="80.0" width="100.0" x="160.0" y="160.0"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-9C8B4BD9-CDFF-4779-8C2F-A3409E285E6B" id="BPMNShape_sid-9C8B4BD9-CDFF-4779-8C2F-A3409E285E6B">
                <omgdc:Bounds height="80.0" width="100.0" x="397.22218890003984" y="70.0"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-80A8FCA9-953C-4A3B-B242-8F4E4677142E" id="BPMNShape_sid-80A8FCA9-953C-4A3B-B242-8F4E4677142E">
                <omgdc:Bounds height="80.00000000000003" width="100.0" x="397.22218890003984" y="249.99999999999997"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-9E4D02B7-8A60-4E6A-AFAD-A976EC2111B3" id="BPMNShape_sid-9E4D02B7-8A60-4E6A-AFAD-A976EC2111B3">
                <omgdc:Bounds height="28.0" width="28.0" x="580.0" y="96.0"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-2EA8D975-04C8-4BC8-9E9B-779589ADFA7F" id="BPMNShape_sid-2EA8D975-04C8-4BC8-9E9B-779589ADFA7F">
                <omgdc:Bounds height="28.0" width="28.0" x="580.0" y="276.0"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-FDBA37A5-9EAA-4BB0-95BF-086EC83B8CDC" id="BPMNShape_sid-FDBA37A5-9EAA-4BB0-95BF-086EC83B8CDC">
                <omgdc:Bounds height="40.0" width="40.0" x="316.0" y="182.0"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNEdge bpmnElement="sid-77140DB2-331F-458C-B0B9-C5C4EFAB73E7" id="BPMNEdge_sid-77140DB2-331F-458C-B0B9-C5C4EFAB73E7">
                <omgdi:waypoint x="497.1721889000398" y="290.0"/>
                <omgdi:waypoint x="580.0" y="290.0"/>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-8BD19FD1-6D24-4C77-9B8C-F6C84D94F0ED" id="BPMNEdge_sid-8BD19FD1-6D24-4C77-9B8C-F6C84D94F0ED">
                <omgdi:waypoint x="336.0" y="182.0"/>
                <omgdi:waypoint x="336.0" y="110.0"/>
                <omgdi:waypoint x="397.22218890003984" y="110.0"/>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-7F7721CC-E147-4FA3-B3F3-474D1D31CD98" id="BPMNEdge_sid-7F7721CC-E147-4FA3-B3F3-474D1D31CD98">
                <omgdi:waypoint x="336.0" y="221.93867763904657"/>
                <omgdi:waypoint x="336.0" y="290.0"/>
                <omgdi:waypoint x="397.22218889997674" y="290.0"/>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-D13A2CBF-4B62-463F-8552-76BC0DCEB04D" id="BPMNEdge_sid-D13A2CBF-4B62-463F-8552-76BC0DCEB04D">
                <omgdi:waypoint x="89.94999899727567" y="200.0"/>
                <omgdi:waypoint x="160.0" y="200.0"/>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-6315F048-1E32-4528-8438-3A9F8F0FE892" id="BPMNEdge_sid-6315F048-1E32-4528-8438-3A9F8F0FE892">
                <omgdi:waypoint x="497.1721888999891" y="110.0"/>
                <omgdi:waypoint x="580.0" y="110.0"/>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-9C5BA16C-5042-477F-B4CD-7476722A96FA" id="BPMNEdge_sid-9C5BA16C-5042-477F-B4CD-7476722A96FA">
                <omgdi:waypoint x="259.95000000000005" y="200.79285714285714"/>
                <omgdi:waypoint x="316.271662763466" y="201.6875"/>
            </bpmndi:BPMNEdge>
        </bpmndi:BPMNPlane>
    </bpmndi:BPMNDiagram>
</definitions>

2.4 引擎配置类

/**
 * 流程引擎配置类
 */
@Configuration
public class ScnProccessEngine {
    

    private static final Logger LOGGER = LoggerFactory.getLogger(ScnProccessEngine.class);

    @Autowired
    private DataSource dataSource;

    @Autowired
    protected ResourceLoader resourceLoader;

    protected static Properties databaseTypeMappings = getDefaultDatabaseTypeMappings();

    protected static final String LIQUIBASE_CHANGELOG_PREFIX = "ACT_DE_";

    public static final String DATABASE_TYPE_H2 = "h2";
    public static final String DATABASE_TYPE_HSQL = "hsql";
    public static final String DATABASE_TYPE_MYSQL = "mysql";
    public static final String DATABASE_TYPE_ORACLE = "oracle";
    public static final String DATABASE_TYPE_POSTGRES = "postgres";
    public static final String DATABASE_TYPE_MSSQL = "mssql";
    public static final String DATABASE_TYPE_DB2 = "db2";

    public static Properties getDefaultDatabaseTypeMappings() {
    
        Properties databaseTypeMappings = new Properties();
        databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2);
        databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL);
        databaseTypeMappings.setProperty("MySQL", DATABASE_TYPE_MYSQL);
        databaseTypeMappings.setProperty("Oracle", DATABASE_TYPE_ORACLE);
        databaseTypeMappings.setProperty("PostgreSQL", DATABASE_TYPE_POSTGRES);
        databaseTypeMappings.setProperty("Microsoft SQL Server", DATABASE_TYPE_MSSQL);
        databaseTypeMappings.setProperty(DATABASE_TYPE_DB2, DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/NT", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/NT64", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2 UDP", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/LINUX", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/LINUX390", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/LINUXX8664", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/LINUXZ64", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/LINUXPPC64", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/400 SQL", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/6000", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2 UDB iSeries", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/AIX64", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/HPUX", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/HP64", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/SUN", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/SUN64", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/PTX", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2/2", DATABASE_TYPE_DB2);
        databaseTypeMappings.setProperty("DB2 UDB AS400", DATABASE_TYPE_DB2);
        return databaseTypeMappings;
    }

    @Bean
    public SpringProcessEngineConfiguration springProcessEngineConfiguration() {
    
        SpringProcessEngineConfiguration springProcessEngineConfiguration = new SpringProcessEngineConfiguration();
        springProcessEngineConfiguration.setDataSource(dataSource);
        springProcessEngineConfiguration.setDatabaseSchemaUpdate("true");
        springProcessEngineConfiguration.setTransactionManager(dataSourceTransactionManager(dataSource));
        return springProcessEngineConfiguration;
    }

    /**
     * 事务管理器
     * @param dataSource
     * @return
     */
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
    
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);
        return dataSourceTransactionManager;
    }

    @Bean
    public FlowableModelerAppProperties flowableModelerAppProperties() {
    
        FlowableModelerAppProperties flowableModelerAppProperties = new FlowableModelerAppProperties();
        return flowableModelerAppProperties;
    }

    /**
     * sqlSessionTemplate的Bean
     * @param sqlSessionFactory
     * @return
     */
    @Bean(destroyMethod = "clearCache") // destroyMethod: see https://github.com/mybatis/old-google-code-issues/issues/778
    public SqlSessionTemplate SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) {
    
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        String databaseType = initDatabaseType(dataSource);
        if (databaseType == null) {
    
            throw new FlowableException("couldn't deduct database type");
        }

        try {
    
            Properties properties = new Properties();
            properties.put("prefix", "");
            properties.put("blobType", "BLOB");
            properties.put("boolValue", "TRUE");

            properties.load(this.getClass().getClassLoader().getResourceAsStream("properties/" + databaseType + ".properties"));

            sqlSessionFactoryBean.setConfigurationProperties(properties);
            sqlSessionFactoryBean
                    .setMapperLocations(ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources("classpath:/META-INF/modeler-mybatis-mappings/*.xml"));
            sqlSessionFactoryBean.afterPropertiesSet();
            return sqlSessionFactoryBean.getObject();
        } catch (Exception e) {
    
            throw new FlowableException("Could not create sqlSessionFactory", e);
        }

    }

    protected String initDatabaseType(DataSource dataSource) {
    
        String databaseType = null;
        Connection connection = null;
        try {
    
            connection = dataSource.getConnection();
            DatabaseMetaData databaseMetaData = connection.getMetaData();
            String databaseProductName = databaseMetaData.getDatabaseProductName();
            LOGGER.info("database product name: '{}'", databaseProductName);
            databaseType = databaseTypeMappings.getProperty(databaseProductName);
            if (databaseType == null) {
    
                throw new FlowableException("couldn't deduct database type from database product name '" + databaseProductName + "'");
            }
            LOGGER.info("using database type: {}", databaseType);

        } catch (SQLException e) {
    
            LOGGER.error("Exception while initializing Database connection", e);
        } finally {
    
            try {
    
                if (connection != null) {
    
                    connection.close();
                }
            } catch (SQLException e) {
    
                LOGGER.error("Exception while closing the Database connection", e);
            }
        }

        return databaseType;
    }

    /**
     * liquibase相关
     * @param dataSource
     * @return
     */
    @Bean
    public Liquibase liquibase(DataSource dataSource) {
    
        LOGGER.info("Configuring Liquibase");

        Liquibase liquibase = null;
        try {
    
            DatabaseConnection connection = new JdbcConnection(dataSource.getConnection());
            Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection);
            database.setDatabaseChangeLogTableName(LIQUIBASE_CHANGELOG_PREFIX + database.getDatabaseChangeLogTableName());
            database.setDatabaseChangeLogLockTableName(LIQUIBASE_CHANGELOG_PREFIX + database.getDatabaseChangeLogLockTableName());

            liquibase = new Liquibase("META-INF/liquibase/flowable-modeler-app-db-changelog.xml", new ClassLoaderResourceAccessor(), database);
            liquibase.update("flowable");
            return liquibase;

        } catch (Exception e) {
    

            throw new InternalServerErrorException("Error creating liquibase database", e);
        } finally {
    
            closeDatabase(liquibase);
        }
    }

    private void closeDatabase(Liquibase liquibase) {
    
        if (liquibase != null) {
    
            Database database = liquibase.getDatabase();
            if (database != null) {
    
                try {
    
                    database.close();
                } catch (DatabaseException e) {
    
                    LOGGER.warn("Error closing database", e);
                }
            }
        }
    }

}

上面写了一些常用的流程引擎以及事务管理的处理。

2.5 公共接口controller

/**
 * Flowable 公共接口
 */
@RestController
@RequestMapping("/pubProccess")
public class PubProccessController {
    

    @Autowired
    private TaskService taskService;

    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    /**
     * 根据用户身份信息查询用户代办任务
     * @param userIdentityPo
     * @return
     */
    @RequestMapping("/queryUserTask")
    public AjaxJson selectUserTaskByUserIdenty(@RequestBody UserIdentityPo userIdentityPo) throws GlobalException {
    
        AjaxJson ajaxJson = new AjaxJson();

        if (userIdentityPo != null && StringUtils.isNotEmpty(userIdentityPo.getFlowUserPrimary())) {
    
            // 最终返回的任务集合
            List<FlowTaskPo> flowTaskPoList = new ArrayList<>();

            String flowUserPrimary = userIdentityPo.getFlowUserPrimary();
            if (StringUtils.isNotEmpty(flowUserPrimary)) {
    
                // 个人任务查询
                List<Task> taskUserList = taskService.createTaskQuery().taskAssignee(flowUserPrimary).list();
                if (taskUserList != null && taskUserList.size() > 0) {
    
                    for (Task task : taskUserList) {
    
                        FlowTaskPo flowTaskPo = new FlowTaskPo();
                        // 代办任务ID
                        String taskId = task.getId();
                        flowTaskPo.setTaskId(taskId);
                        // 任务名称
                        String taskName = task.getName();
                        flowTaskPo.setTaskName(taskName);
                        // 任务创建时间
                        Date taskDate = task.getCreateTime();
                        flowTaskPo.setCreationtime(DateUtils.formatDateTimeToStr(taskDate));
                        // 流程实例主键
                        String processInstanceId = task.getProcessInstanceId();
                        flowTaskPo.setProcessInstanceId(processInstanceId);
                        // 任务类型
                        flowTaskPo.setTaskType(FlowTaskPo.TASK_TYPE_TODO);

                        flowTaskPoList.add(flowTaskPo);
                    }
                }

                // 待认领任务查询
                List<Task> taskGroupList = taskService.createTaskQuery().taskCandidateGroup(flowUserPrimary).list();
                if (taskGroupList != null && taskGroupList.size() > 0) {
    
                    for (Task task : taskGroupList) {
    
                        FlowTaskPo flowTaskPo = new FlowTaskPo();
                        // 代办任务ID
                        String taskId = task.getId();
                        flowTaskPo.setTaskId(taskId);
                        // 任务名称
                        String taskName = task.getName();
                        flowTaskPo.setTaskName(taskName);
                        // 任务创建时间
                        Date taskDate = task.getCreateTime();
                        flowTaskPo.setCreationtime(DateUtils.formatDateTimeToStr(taskDate));
                        // 流程实例主键
                        String processInstanceId = task.getProcessInstanceId();
                        flowTaskPo.setProcessInstanceId(processInstanceId);
                        // 任务类型
                        flowTaskPo.setTaskType(FlowTaskPo.TASK_TYPE_CLAIM);

                        flowTaskPoList.add(flowTaskPo);
                    }
                }

                // 已办任务查询
                List<HistoricTaskInstance> userHistoricTask = processEngine.getHistoryService().createHistoricTaskInstanceQuery().taskAssignee(flowUserPrimary).list();
                for (HistoricTaskInstance historicTaskInstance : userHistoricTask) {
    
                    if (historicTaskInstance != null && historicTaskInstance.getEndTime() != null) {
    
                        FlowTaskPo flowTaskPo = new FlowTaskPo();
                        // 已办任务ID
                        String taskId = historicTaskInstance.getId();
                        flowTaskPo.setTaskId(taskId);
                        // 已办任务名称
                        String taskName = historicTaskInstance.getName();
                        flowTaskPo.setTaskName(taskName);
                        // 任务创建时间
                        Date taskDate = historicTaskInstance.getCreateTime();
                        flowTaskPo.setCreationtime(DateUtils.formatDateTimeToStr(taskDate));
                        // 流程实例主键
                        String processInstanceId = historicTaskInstance.getProcessInstanceId();
                        flowTaskPo.setProcessInstanceId(processInstanceId);
                        // 任务类型
                        flowTaskPo.setTaskType(FlowTaskPo.TASK_TYPE_OVER);

                        flowTaskPoList.add(flowTaskPo);
                    }
                }
            }

            ajaxJson.setObj(flowTaskPoList);
        }

        return ajaxJson;
    }

    /**
     * 用户认领任务
     * @param flowTaskPo
     * @return
     * @throws GlobalException
     */
    @RequestMapping("/claimTask")
    public AjaxJson claimTask(@RequestBody FlowTaskPo flowTaskPo) throws GlobalException {
    
        AjaxJson ajaxJson = new AjaxJson();

        if (flowTaskPo != null
                && StringUtils.isNotEmpty(flowTaskPo.getFlowUserPrimary())
                && StringUtils.isNotEmpty(flowTaskPo.getTaskId())) {
    
            // 任务主键
            String taskId = flowTaskPo.getTaskId();
            // 任务名称
            String taskName = flowTaskPo.getTaskName();
            // 用户主键
            String flowuserPrimary = flowTaskPo.getFlowUserPrimary();

            // 任务认领
            taskService.claim(taskId, flowuserPrimary);
        }

        return ajaxJson;
    }

    /**
     * 查看流程图_高亮
     * @param httpServletResponse
     * @param flowTaskPo
     * @throws GlobalException
     */
    @RequestMapping("/genProcessDiagram")
    public AjaxJson genProcessDiagram(HttpServletResponse httpServletResponse, @RequestBody FlowTaskPo flowTaskPo) throws GlobalException {
    
        AjaxJson ajaxJson = new AjaxJson();

        if (flowTaskPo != null && (StringUtils.isNotEmpty(flowTaskPo.getProcessInstanceId()) || StringUtils.isNotEmpty(flowTaskPo.getTaskId()))) {
    
            String processInstanceId = ""; // 流程实例ID
            String processDefinitionId = ""; // 执行流程DefId
            if (StringUtils.isNotEmpty(flowTaskPo.getProcessInstanceId())) {
    
                processInstanceId = flowTaskPo.getProcessInstanceId();
                ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
                if (processInstance != null) {
    
                    processDefinitionId = processInstance.getProcessDefinitionId();
                } else {
    
                    HistoricProcessInstance historicProcessInstance = processEngine.getHistoryService().createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
                    if (historicProcessInstance != null) {
    
                        processDefinitionId = historicProcessInstance.getProcessDefinitionId();
                    }
                }
            } else {
    
                Task task = taskService.createTaskQuery().taskId(flowTaskPo.getTaskId()).singleResult();
                if (task == null) {
    
                    HistoricTaskInstance historicTaskInstance = processEngine.getHistoryService().createHistoricTaskInstanceQuery().taskId(flowTaskPo.getTaskId()).singleResult();
                    if (historicTaskInstance == null) {
    
                        throw new GlobalException("找不到任务信息!");
                    } else {
    
                        processInstanceId = historicTaskInstance.getProcessInstanceId();
                        processDefinitionId = historicTaskInstance.getProcessDefinitionId();
                    }
                } else {
    
                    processInstanceId = task.getProcessInstanceId();
                    processDefinitionId = task.getProcessDefinitionId();
                }
            }

            Map<String, Object> variables = null;
            try {
    
                variables = processEngine.getRuntimeService().getVariables(processInstanceId);
            } catch (Exception e) {
    
                e.printStackTrace();
            }

            List<HistoricVariableInstance> variableInstanceList = processEngine.getHistoryService().createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list();
            Map<String, String> approveRyMap = new HashMap<>();
            assembleApproveVariables(approveRyMap, variables, variableInstanceList);
            String approveRyJsonStr = JSONObject.toJSONString(approveRyMap);
            String aesValue = ScnAesUtils.enCode(approveRyJsonStr, ScnAesUtils.DEMO_AES_KEY);
            httpServletResponse.addHeader("blobheaderexpand", aesValue);

            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            List<String> activityIds = new ArrayList<>(); // 节点高亮
            List<String> flows = new ArrayList<>(); // 连线高亮
            BpmnModel bpmnModel = null;
            // 流程走完
            if (pi == null) {
    
                bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
            } else {
    
                // 使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
                List<Execution> executions = runtimeService
                        .createExecutionQuery()
                        .processInstanceId(pi.getId())
                        .list();
                // 得到正在执行的Activity的Id
                for (Execution exe : executions) {
    
                    List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
                    activityIds.addAll(ids);
                }

                bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
            }

            // 获取流程图
            ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
            ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
            InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, "宋体", "宋体", "宋体", engconf.getClassLoader(), 1.0, true);
            OutputStream out = null;
            byte[] buf = new byte[1024];
            int legth = 0;
            try {
    
                out = httpServletResponse.getOutputStream();
                while ((legth = in.read(buf)) != -1) {
    
                    out.write(buf, 0, legth);
                }
            } catch (IOException e) {
    
                throw new GlobalException(e.getMessage());
            } finally {
    
                if (in != null) {
    
                    try {
    
                        in.close();
                    } catch (IOException e) {
    
                        throw new GlobalException(e.getMessage());
                    }
                }
                if (out != null) {
    
                    try {
    
                        out.close();
                    } catch (IOException e) {
    
                        throw new GlobalException(e.getMessage());
                    }
                }
            }
        } else {
    
            throw new GlobalException("生成流程图失败,参数错误!");
        }

        return ajaxJson;
    }

    /**
     * 组装审批流程汇总的批语变量
     * @param approveVariables
     * @param mapVariables
     * @param listVariables
     */
    private void assembleApproveVariables(Map<String, String> approveVariables,
                                          Map<String, Object> mapVariables,
                                          List<HistoricVariableInstance> listVariables) {
    
        if (mapVariables != null && mapVariables.size() > 0) {
    
            for(String key : mapVariables.keySet()){
    
                // 建设单位批语
                if ("JIANSHE_APPROVALREPLY".equals(key)) {
    
                    String JIANSHE_APPROVALREPLY = mapVariables.get("JIANSHE_APPROVALREPLY") == null ? "" : mapVariables.get("JIANSHE_APPROVALREPLY").toString();
                    approveVariables.put("JIANSHE_APPROVALREPLY", JIANSHE_APPROVALREPLY);
                }
                // 设计单位批语
                if ("SHEJI_APPROVALREPLY".equals(key)) {
    
                    String SHEJI_APPROVALREPLY = mapVariables.get("SHEJI_APPROVALREPLY") == null ? "" : mapVariables.get("SHEJI_APPROVALREPLY").toString();
                    approveVariables.put("SHEJI_APPROVALREPLY", SHEJI_APPROVALREPLY);
                }
                // 勘察单位批语
                if ("KANCHA_APPROVALREPLY".equals(key)) {
    
                    String KANCHA_APPROVALREPLY = mapVariables.get("KANCHA_APPROVALREPLY") == null ? "" : mapVariables.get("KANCHA_APPROVALREPLY").toString();
                    approveVariables.put("KANCHA_APPROVALREPLY", KANCHA_APPROVALREPLY);
                }
                // 监理单位批语
                if ("JIANLI_APPROVALREPLY".equals(key)) {
    
                    String JIANLI_APPROVALREPLY = mapVariables.get("JIANLI_APPROVALREPLY") == null ? "" : mapVariables.get("JIANLI_APPROVALREPLY").toString();
                    approveVariables.put("JIANLI_APPROVALREPLY", JIANLI_APPROVALREPLY);
                }
                // 施工单位批语
                if ("SHIGONG_APPROVALREPLY".equals(key)) {
    
                    String SHIGONG_APPROVALREPLY = mapVariables.get("SHIGONG_APPROVALREPLY") == null ? "" : mapVariables.get("SHIGONG_APPROVALREPLY").toString();
                    approveVariables.put("SHIGONG_APPROVALREPLY", SHIGONG_APPROVALREPLY);
                }
                // 运管单位批语
                if ("YUNGUAN_APPROVALREPLY".equals(key)) {
    
                    String YUNGUAN_APPROVALREPLY = mapVariables.get("YUNGUAN_APPROVALREPLY") == null ? "" : mapVariables.get("YUNGUAN_APPROVALREPLY").toString();
                    approveVariables.put("YUNGUAN_APPROVALREPLY", YUNGUAN_APPROVALREPLY);
                }
                // 其他单位批语
                if ("OTHER_APPROVALREPLY".equals(key)) {
    
                    String OTHER_APPROVALREPLY = mapVariables.get("OTHER_APPROVALREPLY") == null ? "" : mapVariables.get("OTHER_APPROVALREPLY").toString();
                    approveVariables.put("OTHER_APPROVALREPLY", OTHER_APPROVALREPLY);
                }
            }
        }

        if (listVariables != null && listVariables.size() > 0) {
    
            for (HistoricVariableInstance hisOne : listVariables) {
    
                if (hisOne != null && StringUtils.isNotEmpty(hisOne.getVariableName())) {
    
                    String variableName = hisOne.getVariableName();
                    // 建设单位批语
                    if ("JIANSHE_APPROVALREPLY".equals(variableName)) {
    
                        String JIANSHE_APPROVALREPLY = hisOne.getValue() == null ? "" : hisOne.getValue().toString();
                        approveVariables.put("JIANSHE_APPROVALREPLY", JIANSHE_APPROVALREPLY);
                    }
                    // 设计单位批语
                    if ("SHEJI_APPROVALREPLY".equals(variableName)) {
    
                        String SHEJI_APPROVALREPLY = hisOne.getValue() == null ? "" : hisOne.getValue().toString();
                        approveVariables.put("SHEJI_APPROVALREPLY", SHEJI_APPROVALREPLY);
                    }
                    // 勘察单位批语
                    if ("KANCHA_APPROVALREPLY".equals(variableName)) {
    
                        String KANCHA_APPROVALREPLY = hisOne.getValue() == null ? "" : hisOne.getValue().toString();
                        approveVariables.put("KANCHA_APPROVALREPLY", KANCHA_APPROVALREPLY);
                    }
                    // 监理单位批语
                    if ("JIANLI_APPROVALREPLY".equals(variableName)) {
    
                        String JIANLI_APPROVALREPLY = hisOne.getValue() == null ? "" : hisOne.getValue().toString();
                        approveVariables.put("JIANLI_APPROVALREPLY", JIANLI_APPROVALREPLY);
                    }
                    // 施工单位批语
                    if ("SHIGONG_APPROVALREPLY".equals(variableName)) {
    
                        String SHIGONG_APPROVALREPLY = hisOne.getValue() == null ? "" : hisOne.getValue().toString();
                        approveVariables.put("SHIGONG_APPROVALREPLY", SHIGONG_APPROVALREPLY);
                    }
                    // 运管单位批语
                    if ("YUNGUAN_APPROVALREPLY".equals(variableName)) {
    
                        String YUNGUAN_APPROVALREPLY = hisOne.getValue() == null ? "" : hisOne.getValue().toString();
                        approveVariables.put("YUNGUAN_APPROVALREPLY", YUNGUAN_APPROVALREPLY);
                    }
                    // 其他单位批语
                    if ("OTHER_APPROVALREPLY".equals(variableName)) {
    
                        String OTHER_APPROVALREPLY = hisOne.getValue() == null ? "" : hisOne.getValue().toString();
                        approveVariables.put("OTHER_APPROVALREPLY", OTHER_APPROVALREPLY);
                    }
                }
            }
        }
    }

}

上文写了一些公共的审批查看流程图等常用的方法。


3. 支持的数据库

flowable支持的数据看有很多种
以下是官方给的支持的数据库的类型:

在这里插入图片描述
flowable的默认的数据库是h2 所以使用的时候需要对h2数据库有个简单的了解。

  H2数据库是一个开源的Java数据库管理系统,它可以与各种关系型数据库进行集成,如MySQL、Oracle、PostgreSQL等。H2数据库是基于Java语言编写的,可以在Java应用程序中嵌入使用,也可以作为一个独立的数据库服务器运行。

H2数据库的特点包括:

  1. 轻量级:H2数据库非常轻量级,可以在内存中运行,不需要安装和配置外部数据库服务器。
  2. 快速:H2数据库支持高性能的JDBC驱动程序,可以实现快速的数据访问和查询。
  3. 开源免费:H2数据库是开源软件,可以免费使用和修改。
  4. 支持嵌入式:H2数据库可以嵌入到Java应用程序中使用,方便开发人员在应用程序中使用本地数据库。

  总之,H2数据库是一个轻量级、高性能、开源免费的Java数据库管理系统,适合用于开发嵌入式应用程序或小型项目中的数据存储和管理。


总结:

flowable 注意事项:
  flowable会为我们自动创建表,用于数据,历史记录,流程定义,运行实例等等。 如果启动失败,解决错误以后,每次都需要把表数据清空,或者删除之后再重新启动。


如果喜欢的话,欢迎 关注 点赞 评论 收藏 一起讨论
你的评价就是我️创作的动力!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wodejiaAA/article/details/130756122

智能推荐

Android Studio 基础 之 截图,指定截图区域动态截图_android studio 截图-程序员宅基地

文章浏览阅读4.5k次,点赞5次,收藏33次。Android Studio 基础 之 截图,指定截图区域动态截图目录Android Studio 基础 之 截图,指定截图区域动态截图一、简单介绍二、实现原理三、注意事项四、预览效果五、实现步骤六、关键代码7、附录简版截图下面是简单的只要 x, y, width,heigth 输入进行屏幕截图的代码(也是从上面代码截取出来的,方便后期封装为工具类)一、简单介绍Android 开发中的一些基础操作,使用整理,便于后期使用。本节介绍,在Android中,_android studio 截图

android的底层驱动调试心得_cat /d/gpio-程序员宅基地

文章浏览阅读3.5k次,点赞3次,收藏23次。1、安卓模拟器使用sudo snap install scrcpy_302.snap --dangerousscrcpy抓取gpio的状态cat sys/kernel/debug/gpio2、调试背光ifconfig -a 查看wifi是否起来fdb111c89802e2bd78ebacaedac8e56ab12704ce lams0c7ba9436091827cc233b197c1432f4af8b0f1fe cq#sudo upgrade_tool di -b boot.img_cat /d/gpio

iOS 导航条 知识简析(返回按钮,标题,背景颜色 等)_返回按钮一般是什么颜色-程序员宅基地

文章浏览阅读2.4k次。一:导航条的返回按钮在讲导航条的返回按钮之前,先分享一个发现: 导航条自带 pop手势滑动功能,如果你没有自定义“返回”按钮,则可以直接手势滑动。栗子:页面A push到页面 B, B要pop回到A,在没有自定义返回按钮的情况下,可以直接点击导航条的Back按钮,也可以讲手指放在屏幕左边,向右滑动屏幕,即可pop回A。注:如果自定义了导航条的返回按钮,则滑动功能消失_返回按钮一般是什么颜色

技巧收集-程序员宅基地

文章浏览阅读762次。判断用户是否中止并离开了RESPONSE.ISCLIENTCONNECTED

盒子边框、外边距、内边距以及浮动设置_dorder-程序员宅基地

文章浏览阅读1.7k次。1.浮动设置 float:浮动方向 right右浮动 left左浮动。 2.边框设置 dorder:宽度 样式 颜色 ,其中边框实线为solid,虚线为dotted。3.外边距设置 margin:距离 1-4个值都可以,顺时针赋值,或者 margin..._dorder

SVD矩阵分解_svd分解-程序员宅基地

文章浏览阅读2.5k次。SVD矩阵在信息科学中的应用_svd分解

随便推点

操作系统精选习题——第四章_系统抖动现象的发生由什么引起的-程序员宅基地

文章浏览阅读3.4k次,点赞3次,收藏29次。一.单选题二.填空题三.判断题一.单选题静态链接是在( )进行的。A、编译某段程序时B、装入某段程序时C、紧凑时D、装入程序之前Pentium处理器(32位)最大可寻址的虚拟存储器地址空间为( )。A、由内存的容量而定B、4GC、2GD、1G分页系统中,主存分配的单位是( )。A、字节B、物理块C、作业D、段在段页式存储管理中,当执行一段程序时,至少访问()次内存。A、1B、2C、3D、4在分段管理中,( )。A、以段为单位分配,每._系统抖动现象的发生由什么引起的

UG NX 12零件工程图基础_ug-nx工程图-程序员宅基地

文章浏览阅读2.4k次。在实际的工作生产中,零件的加工制造一般都需要二维工程图来辅助设计。UG NX 的工程图主要是为了满足二维出图需要。在绘制工程图时,需要先确定所绘制图形要表达的内容,然后根据需要并按照视图的选择原则,绘制工程图的主视图、其他视图以及某些特殊视图,最后标注图形的尺寸、技术说明等信息,即可完成工程图的绘制。1.视图选择原则工程图合理的表达方案要综合运用各种表达方法,清晰完整地表达出零件的结构形状,并便于看图。确定工程图表达方案的一般步骤如下:口分析零件结构形状由于零件的结构形状以及加工位置或工作位置的不._ug-nx工程图

智能制造数字化工厂智慧供应链大数据解决方案(PPT)-程序员宅基地

文章浏览阅读920次,点赞29次,收藏18次。原文《智能制造数字化工厂智慧供应链大数据解决方案》PPT格式主要从智能制造数字化工厂智慧供应链大数据解决方案框架图、销量预测+S&OP大数据解决方案、计划统筹大数据解决方案、订单履约大数据解决方案、库存周转大数据解决方案、采购及供应商管理大数据模块、智慧工厂大数据解决方案、设备管理大数据解决方案、质量管理大数据解决方案、仓储物流与网络优化大数据解决方案、供应链决策分析大数据解决方案进行建设。适用于售前项目汇报、项目规划、领导汇报。

网络编程socket accept函数的理解_当在函数 'main' 中调用 'open_socket_accept'时.line: 8. con-程序员宅基地

文章浏览阅读2w次,点赞38次,收藏102次。在服务器端,socket()返回的套接字用于监听(listen)和接受(accept)客户端的连接请求。这个套接字不能用于与客户端之间发送和接收数据。 accept()接受一个客户端的连接请求,并返回一个新的套接字。所谓“新的”就是说这个套接字与socket()返回的用于监听和接受客户端的连接请求的套接字不是同一个套接字。与本次接受的客户端的通信是通过在这个新的套接字上发送和接收数_当在函数 'main' 中调用 'open_socket_accept'时.line: 8. connection request fa

C#对象销毁_c# 销毁对象及其所有引用-程序员宅基地

文章浏览阅读4.3k次。对象销毁对象销毁的标准语法Close和Stop何时销毁对象销毁对象时清除字段对象销毁的标准语法Framework在销毁对象的逻辑方面遵循一套规则,这些规则并不限用于.NET Framework或C#语言;这些规则的目的是定义一套便于使用的协议。这些协议如下:一旦销毁,对象不可恢复。对象不能被再次激活,调用对象的方法或者属性抛出ObjectDisposedException异常重复地调用对象的Disposal方法会导致错误如果一个可销毁对象x 包含或包装或处理另外一个可销毁对象y,那么x的Disp_c# 销毁对象及其所有引用

笔记-中项/高项学习期间的错题笔记1_大型设备可靠性测试可否拆解为几个部分进行测试-程序员宅基地

文章浏览阅读1.1w次。这是记录,在中项、高项过程中的错题笔记;https://www.zenwu.site/post/2b6d.html1. 信息系统的规划工具在制订计划时,可以利用PERT图和甘特图;访谈时,可以应用各种调查表和调查提纲;在确定各部门、各层管理人员的需求,梳理流程时,可以采用会谈和正式会议的方法。为把企业组织结构与企业过程联系起来,说明每个过程与组织的联系,指出过程决策人,可以采用建立过程/组织(Process/Organization,P/O)矩阵的方法。例如,一个简单的P/O矩阵示例,其中._大型设备可靠性测试可否拆解为几个部分进行测试