一文解读flowable工作流

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会为我们自动创建表,用于数据,历史记录,流程定义,运行实例等等。 如果启动失败,解决错误以后,每次都需要把表数据清空,或者删除之后再重新启动。


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

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

家有娇妻张兔兔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值