flowable工作流的定义 ,流程 及项目使用
🏵️flowable工作流
🌜1. flowable
☁️ 1.1 定义
Flowable是一个开源的业务流程管理(BPM)平台,它可以帮助开发人员和业务专家快速构建、部署和管理复杂的业务流程。Flowable提供了一个可视化的工作流设计器,可以方便地定义工作流程、任务、网关、决策等元素,并支持多种流程引擎的选择,如Activiti、Camunda等。
Flowable的核心特性包括:
- 可视化工作流设计器:Flowable提供了一个直观、易用的工作流设计器,可以方便地定义工作流程、任务、网关、决策等元素。
- 多引擎支持:Flowable支持多种流程引擎的选择,如Activiti、Camunda等,可以根据应用程序的需求进行选择。
- 扩展性:Flowable提供了丰富的API和插件,可以方便地进行定制和扩展。
- 集成性: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的流程通常由以下几个步骤组成:
- 定义工作流:使用Flowable的工作流设计器定义工作流,包括流程定义、任务、网关、决策等元素。
- 配置流程引擎:选择合适的流程引擎,如Activiti、Camunda等,并进行相应的配置和集成。
- 创建流程实例:根据工作流定义创建一个新的流程实例,并将任务分配给相应的人员或组。
- 执行流程:流程实例启动后,会按照预定义的流程进行执行,包括任务完成、网关判断、决策结果等。
- 监控流程:可以使用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数据库的特点包括:
- 轻量级:H2数据库非常轻量级,可以在内存中运行,不需要安装和配置外部数据库服务器。
- 快速:H2数据库支持高性能的JDBC驱动程序,可以实现快速的数据访问和查询。
- 开源免费:H2数据库是开源软件,可以免费使用和修改。
- 支持嵌入式:H2数据库可以嵌入到Java应用程序中使用,方便开发人员在应用程序中使用本地数据库。
总之,H2数据库是一个轻量级、高性能、开源免费的Java数据库管理系统,适合用于开发嵌入式应用程序或小型项目中的数据存储和管理。
🚩 总结:
flowable 注意事项:
flowable会为我们自动创建表,用于数据,历史记录,流程定义,运行实例等等。 如果启动失败,解决错误以后,每次都需要把表数据清空,或者删除之后再重新启动。
如果喜欢的话,欢迎 🤞关注 👍点赞 💬评论 🤝收藏 🙌一起讨论
你的评价就是我✍️创作的动力! 💞💞💞