This commit is contained in:
2025-11-28 16:23:32 +08:00
commit a9e0e16c29
826 changed files with 89805 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>BladeX-Tool</artifactId>
<groupId>org.springblade</groupId>
<version>${revision}</version>
</parent>
<artifactId>blade-starter-flowable</artifactId>
<name>${project.artifactId}</name>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
<properties>
<flowable.version>7.0.1</flowable.version>
</properties>
<dependencies>
<!-- 工作流 -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>${flowable.version}</version>
</dependency>
<dependency>
<groupId>net.tirasa.flowable-leftovers</groupId>
<artifactId>flowable-json-converter</artifactId>
<version>${flowable.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,218 @@
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.flowable.common.engine.impl.db;
import liquibase.Liquibase;
import liquibase.Scope;
import liquibase.database.Database;
import liquibase.database.DatabaseConnection;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.DatabaseException;
import liquibase.resource.ClassLoaderResourceAccessor;
import liquibase.ui.LoggerUIService;
import org.apache.commons.lang3.StringUtils;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.impl.AbstractEngineConfiguration;
import org.flowable.common.engine.impl.context.Context;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ClassUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
/**
* @author Filip Hrisafov
*/
public abstract class LiquibaseBasedSchemaManager implements SchemaManager {
private static final String LIQUIBASE_HUB_SERVICE_CLASS_NAME = "liquibase.hub.HubService";
protected static final Map<String, Object> LIQUIBASE_SCOPE_VALUES = new HashMap<>();
static {
if (ClassUtils.isPresent(LIQUIBASE_HUB_SERVICE_CLASS_NAME, null)) {
LIQUIBASE_SCOPE_VALUES.put("liquibase.plugin." + LIQUIBASE_HUB_SERVICE_CLASS_NAME, FlowableLiquibaseHubService.class);
LoggerUIService uiService = new LoggerUIService();
uiService.setStandardLogLevel(Level.FINE);
LIQUIBASE_SCOPE_VALUES.put(Scope.Attr.ui.name(), uiService);
}
}
protected final Logger logger = LoggerFactory.getLogger(getClass());
protected final String context;
protected final String changeLogFile;
protected final String changeLogPrefix;
public LiquibaseBasedSchemaManager(String context, String changeLogFile, String changeLogPrefix) {
this.context = context;
this.changeLogFile = changeLogFile;
this.changeLogPrefix = changeLogPrefix;
}
public void initSchema(String databaseSchemaUpdate) {
try {
if (AbstractEngineConfiguration.DB_SCHEMA_UPDATE_CREATE.equals(databaseSchemaUpdate)) {
runForLiquibase(this::schemaCreate);
}
else if (AbstractEngineConfiguration.DB_SCHEMA_UPDATE_CREATE_DROP.equals(databaseSchemaUpdate)) {
runForLiquibase(this::schemaCreate);
} else if (AbstractEngineConfiguration.DB_SCHEMA_UPDATE_DROP_CREATE.equals(databaseSchemaUpdate)) {
runForLiquibase(() -> {
schemaDrop();
schemaCreate();
});
} else if (AbstractEngineConfiguration.DB_SCHEMA_UPDATE_TRUE.equals(databaseSchemaUpdate)) {
runForLiquibase(this::schemaUpdate);
} else if (AbstractEngineConfiguration.DB_SCHEMA_UPDATE_FALSE.equals(databaseSchemaUpdate)) {
//取消自检查
//runForLiquibase(this::schemaCheckVersion);
}
} catch (Exception e) {
throw new FlowableException("Error initialising " + context + " data model", e);
}
}
protected void runForLiquibase(Runnable runnable) throws Exception {
Scope.child(LIQUIBASE_SCOPE_VALUES, runnable::run);
}
@Override
public void schemaCreate() {
Liquibase liquibase = null;
try {
liquibase = createLiquibaseInstance(getDatabaseConfiguration());
liquibase.update(context);
} catch (Exception e) {
throw new FlowableException("Error creating " + context + " engine tables", e);
} finally {
closeDatabase(liquibase);
}
}
@Override
public void schemaDrop() {
Liquibase liquibase = null;
try {
liquibase = createLiquibaseInstance(getDatabaseConfiguration());
liquibase.dropAll();
} catch (Exception e) {
throw new FlowableException("Error dropping " + context + " engine tables", e);
} finally {
closeDatabase(liquibase);
}
}
@Override
public String schemaUpdate() {
Liquibase liquibase = null;
try {
liquibase = createLiquibaseInstance(getDatabaseConfiguration());
liquibase.update(context);
} catch (Exception e) {
throw new FlowableException("Error updating " + context + " engine tables", e);
} finally {
closeDatabase(liquibase);
}
return null;
}
@Override
public void schemaCheckVersion() {
Liquibase liquibase = null;
try {
liquibase = createLiquibaseInstance(getDatabaseConfiguration());
liquibase.validate();
} catch (Exception e) {
throw new FlowableException("Error validating " + context + " engine schema", e);
} finally {
closeDatabase(liquibase);
}
}
protected abstract LiquibaseDatabaseConfiguration getDatabaseConfiguration();
protected Liquibase createLiquibaseInstance(LiquibaseDatabaseConfiguration databaseConfiguration) throws SQLException {
Connection jdbcConnection = null;
boolean closeConnection = false;
try {
CommandContext commandContext = Context.getCommandContext();
if (commandContext == null) {
jdbcConnection = databaseConfiguration.getDataSource().getConnection();
closeConnection = true;
} else {
jdbcConnection = commandContext.getSession(DbSqlSession.class).getSqlSession().getConnection();
}
// A commit is needed here, because one of the things that Liquibase does when acquiring its lock
// is doing a rollback, which removes all changes done so far.
// For most databases, this is not a problem as DDL statements are not transactional.
// However for some (e.g. sql server), this would remove all previous statements, which is not wanted,
// hence the extra commit here.
if (!jdbcConnection.getAutoCommit()) {
jdbcConnection.commit();
}
DatabaseConnection connection = new JdbcConnection(jdbcConnection);
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection);
database.setDatabaseChangeLogTableName(changeLogPrefix + database.getDatabaseChangeLogTableName());
database.setDatabaseChangeLogLockTableName(changeLogPrefix + database.getDatabaseChangeLogLockTableName());
String databaseSchema = databaseConfiguration.getDatabaseSchema();
if (StringUtils.isNotEmpty(databaseSchema)) {
database.setDefaultSchemaName(databaseSchema);
database.setLiquibaseSchemaName(databaseSchema);
}
String databaseCatalog = databaseConfiguration.getDatabaseCatalog();
if (StringUtils.isNotEmpty(databaseCatalog)) {
database.setDefaultCatalogName(databaseCatalog);
database.setLiquibaseCatalogName(databaseCatalog);
}
return new Liquibase(changeLogFile, new ClassLoaderResourceAccessor(), database);
} catch (Exception e) {
// We only close the connection if an exception occurred, otherwise the Liquibase instance cannot be used
if (jdbcConnection != null && closeConnection) {
jdbcConnection.close();
}
throw new FlowableException("Error creating " + context + " liquibase instance", e);
}
}
protected void closeDatabase(Liquibase liquibase) {
if (liquibase != null) {
Database database = liquibase.getDatabase();
if (database != null) {
// do not close the shared connection if a command context is currently active
if (Context.getCommandContext() == null) {
try {
database.close();
} catch (DatabaseException e) {
logger.warn("Error closing database for {}", context, e);
}
}
}
}
}
}

View File

@@ -0,0 +1,53 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.flowable.editor.language.json.converter;
import java.util.Map;
/**
* BpmnJsonCustomConverter
*
* @author Chill
*/
public class CustomBpmnJsonConverterContext extends StandaloneBpmnConverterContext {
private final Map<String, String> formKeyMap;
private final Map<String, String> decisionTableKeyMap;
public CustomBpmnJsonConverterContext(Map<String, String> formKeyMap, Map<String, String> decisionTableKeyMap) {
this.formKeyMap = formKeyMap;
this.decisionTableKeyMap = decisionTableKeyMap;
}
@Override
public String getFormModelKeyForFormModelId(String formModelId) {
return formKeyMap.get(formModelId);
}
@Override
public String getDecisionTableModelKeyForDecisionTableModelId(String decisionTableModelId) {
return decisionTableKeyMap.get(decisionTableModelId);
}
}

View File

@@ -0,0 +1,123 @@
<?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="Leave" name="请假流程" isExecutable="true">
<documentation>请假流程</documentation>
<startEvent id="start" name="开始" flowable:initiator="applyUser"></startEvent>
<userTask id="hrTask" name="人事审批" flowable:assignee="${taskUser}">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<exclusiveGateway id="judgeTask"></exclusiveGateway>
<userTask id="managerTak" name="经理审批" flowable:candidateGroups="manager"></userTask>
<userTask id="bossTask" name="老板审批" flowable:candidateGroups="boss"></userTask>
<endEvent id="end" name="结束"></endEvent>
<sequenceFlow id="flow1" sourceRef="start" targetRef="hrTask"></sequenceFlow>
<sequenceFlow id="managerPassFlow" name="通过" sourceRef="managerTak" targetRef="end">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${pass}]]></conditionExpression>
</sequenceFlow>
<userTask id="userTask" name="调整申请" flowable:assignee="${applyUser}">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<sequenceFlow id="bossPassFlow" name="通过" sourceRef="bossTask" targetRef="end">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${pass}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="judgeMore" name="大于3天" sourceRef="judgeTask" targetRef="bossTask">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${days > 3}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="managerNotPassFlow" name="驳回" sourceRef="managerTak" targetRef="userTask">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!pass}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="bossNotPassFlow" name="驳回" sourceRef="bossTask" targetRef="userTask">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!pass}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="hrPassFlow" name="同意" sourceRef="hrTask" targetRef="judgeTask">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${pass}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="hrNotPassFlow" name="驳回" sourceRef="hrTask" targetRef="userTask">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!pass}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="judgeLess" name="小于3天" sourceRef="judgeTask" targetRef="managerTak">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${days <= 3}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="userPassFlow" name="重新申请" sourceRef="userTask" targetRef="hrTask">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${pass}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="userNotPassFlow" name="关闭申请" sourceRef="userTask" targetRef="end">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!pass}]]></conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_Leave">
<bpmndi:BPMNPlane bpmnElement="Leave" id="BPMNPlane_Leave">
<bpmndi:BPMNShape bpmnElement="start" id="BPMNShape_start">
<omgdc:Bounds height="30.0" width="30.0" x="300.0" y="135.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="hrTask" id="BPMNShape_hrTask">
<omgdc:Bounds height="80.0" width="100.0" x="360.0" y="165.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="judgeTask" id="BPMNShape_judgeTask">
<omgdc:Bounds height="40.0" width="40.0" x="255.0" y="300.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="managerTak" id="BPMNShape_managerTak">
<omgdc:Bounds height="80.0" width="100.0" x="555.0" y="75.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="bossTask" id="BPMNShape_bossTask">
<omgdc:Bounds height="80.0" width="100.0" x="450.0" y="420.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="end" id="BPMNShape_end">
<omgdc:Bounds height="28.0" width="28.0" x="705.0" y="390.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="userTask" id="BPMNShape_userTask">
<omgdc:Bounds height="80.0" width="100.0" x="510.0" y="270.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="327.9390183144677" y="157.4917313275668"></omgdi:waypoint>
<omgdi:waypoint x="360.0" y="176.05263157894737"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="hrPassFlow" id="BPMNEdge_hrPassFlow">
<omgdi:waypoint x="363.04347826086956" y="244.95000000000002"></omgdi:waypoint>
<omgdi:waypoint x="285.77299999999997" y="310.79999999999995"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="hrNotPassFlow" id="BPMNEdge_hrNotPassFlow">
<omgdi:waypoint x="459.95" y="236.21875000000006"></omgdi:waypoint>
<omgdi:waypoint x="513.9794844818516" y="270.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="judgeLess" id="BPMNEdge_judgeLess">
<omgdi:waypoint x="274.3359375" y="300.66397214564284"></omgdi:waypoint>
<omgdi:waypoint x="274.3359375" y="115.0"></omgdi:waypoint>
<omgdi:waypoint x="554.9999999999982" y="115.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="userPassFlow" id="BPMNEdge_userPassFlow">
<omgdi:waypoint x="510.0" y="310.0"></omgdi:waypoint>
<omgdi:waypoint x="411.0" y="310.0"></omgdi:waypoint>
<omgdi:waypoint x="411.0" y="244.95000000000002"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="bossPassFlow" id="BPMNEdge_bossPassFlow">
<omgdi:waypoint x="549.9499999999998" y="447.2146118721461"></omgdi:waypoint>
<omgdi:waypoint x="705.4331577666419" y="407.4567570622598"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="judgeMore" id="BPMNEdge_judgeMore">
<omgdi:waypoint x="287.29730895645025" y="327.65205479452055"></omgdi:waypoint>
<omgdi:waypoint x="450.0" y="428.8888888888889"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="managerPassFlow" id="BPMNEdge_managerPassFlow">
<omgdi:waypoint x="620.7588235294118" y="154.95"></omgdi:waypoint>
<omgdi:waypoint x="713.8613704477151" y="390.96328050279476"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="userNotPassFlow" id="BPMNEdge_userNotPassFlow">
<omgdi:waypoint x="609.95" y="339.5301886792453"></omgdi:waypoint>
<omgdi:waypoint x="706.9383699359797" y="396.87411962686997"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="bossNotPassFlow" id="BPMNEdge_bossNotPassFlow">
<omgdi:waypoint x="515.98" y="420.0"></omgdi:waypoint>
<omgdi:waypoint x="544.0" y="349.95000000000005"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="managerNotPassFlow" id="BPMNEdge_managerNotPassFlow">
<omgdi:waypoint x="595.438344721373" y="154.95"></omgdi:waypoint>
<omgdi:waypoint x="567.9366337262223" y="270.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>