From f95c7b8695bb7852ee51c1c30896980a9a532a29 Mon Sep 17 00:00:00 2001 From: Jerry <707344974@qq.com> Date: Fri, 11 Oct 2024 21:43:29 +0800 Subject: [PATCH] =?UTF-8?q?commit=EF=BC=9A=E6=B5=81=E7=A8=8B=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E8=87=AA=E5=8A=A8=E5=8C=96=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/constant/ApplicationConstant.java | 4 + .../common/core/util/MyCommonUtil.java | 15 +- .../common/dbutil/util/DataSourceUtil.java | 118 +++- .../common/common-flow/pom.xml | 5 + .../flow/constant/FlowApprovalType.java | 4 + .../flow/constant/FlowAutoActionType.java | 69 ++ .../common/flow/constant/FlowConstant.java | 10 + .../flow/controller/FlowDblinkController.java | 276 ++++++++ .../controller/FlowOperationController.java | 22 + .../flow/dao/FlowAutoVariableLogMapper.java | 13 + .../common/flow/dao/FlowDblinkMapper.java | 26 + .../flow/dao/FlowTransProducerMapper.java | 13 + .../dao/mapper/FlowAutoVariableLogMapper.xml | 14 + .../flow/dao/mapper/FlowDblinkMapper.xml | 47 ++ .../flow/dao/mapper/FlowEntryMapper.xml | 5 + .../flow/dao/mapper/FlowTaskExtMapper.xml | 4 +- .../dao/mapper/FlowTransProducerMapper.xml | 25 + .../common/flow/dto/FlowDblinkDto.java | 53 ++ .../flow/listener/ReceiveTaskEndListener.java | 86 +++ .../listener/ReceiveTaskStartListener.java | 46 ++ .../flow/model/FlowAutoVariableLog.java | 67 ++ .../common/flow/model/FlowDblink.java | 87 +++ .../common/flow/model/FlowEntry.java | 6 + .../common/flow/model/FlowEntryVariable.java | 10 + .../common/flow/model/FlowTaskExt.java | 6 + .../common/flow/model/FlowTransProducer.java | 133 ++++ .../flow/model/constant/FlowEntryType.java | 44 ++ .../flow/model/constant/FlowVariableType.java | 10 + .../common/flow/object/AutoExecData.java | 28 + .../flow/object/AutoHttpRequestInfo.java | 53 ++ .../flow/object/AutoHttpResponseData.java | 38 + .../common/flow/object/AutoTaskConfig.java | 180 +++++ .../common/flow/object/AutoTaskVariable.java | 43 ++ .../common/flow/service/FlowApiService.java | 21 +- .../service/FlowAutoVariableLogService.java | 28 + .../flow/service/FlowDblinkService.java | 89 +++ .../flow/service/FlowTaskExtService.java | 7 + .../service/FlowTransProducerService.java | 21 + .../flow/service/impl/FlowApiServiceImpl.java | 81 ++- .../impl/FlowAutoVariableLogServiceImpl.java | 53 ++ .../service/impl/FlowCategoryServiceImpl.java | 1 + .../service/impl/FlowDblinkServiceImpl.java | 142 ++++ .../service/impl/FlowEntryServiceImpl.java | 37 +- .../service/impl/FlowTaskExtServiceImpl.java | 113 ++- .../impl/FlowTransProducerServiceImpl.java | 68 ++ .../common/flow/util/AutoFlowHelper.java | 657 ++++++++++++++++++ .../common/flow/util/FlowDataSourceUtil.java | 47 ++ .../flow/util/ListenerEventPublishHelper.java | 59 ++ .../common/flow/vo/FlowDblinkVo.java | 84 +++ .../zz-resource/.DS_Store | Bin 0 -> 6148 bytes .../db-scripts/zzdemo-online-open.sql | 68 ++ 51 files changed, 3094 insertions(+), 42 deletions(-) create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/constant/FlowAutoActionType.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/controller/FlowDblinkController.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/FlowAutoVariableLogMapper.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/FlowDblinkMapper.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/FlowTransProducerMapper.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowAutoVariableLogMapper.xml create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowDblinkMapper.xml create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowTransProducerMapper.xml create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dto/FlowDblinkDto.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/listener/ReceiveTaskEndListener.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/listener/ReceiveTaskStartListener.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowAutoVariableLog.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowDblink.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowTransProducer.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/constant/FlowEntryType.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoExecData.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoHttpRequestInfo.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoHttpResponseData.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoTaskConfig.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoTaskVariable.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowAutoVariableLogService.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowDblinkService.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowTransProducerService.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowAutoVariableLogServiceImpl.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowDblinkServiceImpl.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowTransProducerServiceImpl.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/util/AutoFlowHelper.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/util/FlowDataSourceUtil.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/util/ListenerEventPublishHelper.java create mode 100644 OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/vo/FlowDblinkVo.java create mode 100644 OrangeFormsOpen-MybatisPlus/zz-resource/.DS_Store diff --git a/OrangeFormsOpen-MybatisPlus/common/common-core/src/main/java/com/orangeforms/common/core/constant/ApplicationConstant.java b/OrangeFormsOpen-MybatisPlus/common/common-core/src/main/java/com/orangeforms/common/core/constant/ApplicationConstant.java index 25fce820..35993ebb 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-core/src/main/java/com/orangeforms/common/core/constant/ApplicationConstant.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-core/src/main/java/com/orangeforms/common/core/constant/ApplicationConstant.java @@ -78,6 +78,10 @@ public final class ApplicationConstant { * 请求头跟踪id名。 */ public static final String HTTP_HEADER_TRACE_ID = "traceId"; + /** + * 请求头业务流水号id名。 + */ + public static final String HTTP_HEADER_TRANS_ID = "transId"; /** * 请求头菜单Id。 */ diff --git a/OrangeFormsOpen-MybatisPlus/common/common-core/src/main/java/com/orangeforms/common/core/util/MyCommonUtil.java b/OrangeFormsOpen-MybatisPlus/common/common-core/src/main/java/com/orangeforms/common/core/util/MyCommonUtil.java index fa97c514..b10156a6 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-core/src/main/java/com/orangeforms/common/core/util/MyCommonUtil.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-core/src/main/java/com/orangeforms/common/core/util/MyCommonUtil.java @@ -433,7 +433,20 @@ public class MyCommonUtil { builder.append("'"); return builder.toString(); } - + + /** + * 获取当前请求的traceId。 + * + * @return 当前请求的traceId。 + */ + public static String getTraceId() { + HttpServletRequest request = ContextUtil.getHttpRequest(); + if (request == null) { + return null; + } + return request.getHeader(ApplicationConstant.HTTP_HEADER_TRACE_ID); + } + /** * 私有构造函数,明确标识该常量类的作用。 */ diff --git a/OrangeFormsOpen-MybatisPlus/common/common-dbutil/src/main/java/com/orangeforms/common/dbutil/util/DataSourceUtil.java b/OrangeFormsOpen-MybatisPlus/common/common-dbutil/src/main/java/com/orangeforms/common/dbutil/util/DataSourceUtil.java index a3ca1445..cb04c166 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-dbutil/src/main/java/com/orangeforms/common/dbutil/util/DataSourceUtil.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-dbutil/src/main/java/com/orangeforms/common/dbutil/util/DataSourceUtil.java @@ -1,11 +1,14 @@ package com.orangeforms.common.dbutil.util; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidDataSourceFactory; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.orangeforms.common.core.constant.FieldFilterType; import com.orangeforms.common.core.exception.InvalidDblinkTypeException; @@ -30,6 +33,7 @@ import net.sf.jsqlparser.statement.select.SelectItem; import org.joda.time.DateTime; import javax.sql.DataSource; +import java.io.Serializable; import java.sql.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -50,12 +54,12 @@ public abstract class DataSourceUtil { private static final Map PROVIDER_MAP = new HashMap<>(5); protected final Map dblinkProviderMap = new ConcurrentHashMap<>(4); - private static final String SQL_SELECT = " SELECT "; - private static final String SQL_SELECT_FROM = " SELECT * FROM ("; - private static final String SQL_AS_TMP = " ) tmp "; - private static final String SQL_ORDER_BY = " ORDER BY "; - private static final String SQL_AND = " AND "; - private static final String SQL_WHERE = " WHERE "; + public static final String SQL_SELECT = " SELECT "; + public static final String SQL_SELECT_FROM = " SELECT * FROM ("; + public static final String SQL_AS_TMP = " ) tmp "; + public static final String SQL_ORDER_BY = " ORDER BY "; + public static final String SQL_AND = " AND "; + public static final String SQL_WHERE = " WHERE "; private static final String LOG_PREPARING_FORMAT = "==> Preparing: {}"; private static final String LOG_PARMS_FORMAT = "==> Parameters: {}"; private static final String LOG_TOTAL_FORMAT = "<== Total: {}"; @@ -356,6 +360,27 @@ public abstract class DataSourceUtil { return this.getDataListInternnally(dblinkId, provider, sqlCount, sql, datasetParam, paramList); } + /** + * 执行包含参数变量的增删改操作。 + * + * @param connection 数据库链接。 + * @param sql SQL语句。 + * @param paramList 参数列表。 + * @return 影响的行数。 + */ + public int execute(Connection connection, String sql, List paramList) { + try (PreparedStatement stat = connection.prepareStatement(sql)) { + for (int i = 0; i < paramList.size(); i++) { + stat.setObject(i + 1, paramList.get(i)); + } + stat.execute(); + return stat.getUpdateCount(); + } catch (SQLException e) { + log.error(e.getMessage(), e); + throw new MyRuntimeException(e); + } + } + /** * 在指定数据库链接上执行查询语句,并返回指定映射对象类型的单条数据对象。 * @@ -416,6 +441,87 @@ public abstract class DataSourceUtil { } } + /** + * 根据数据库链接的类型,将数据表字段类型转换为Java的字段属性类型。 + * + * @param column 数据表字段对象。 + * @param dblinkType 数据库链接类型。 + * @return 返回与Java字段属性的类型名。 + */ + public String convertToJavaType(SqlTableColumn column, int dblinkType) { + return this.convertToJavaType( + column.getColumnType(), column.getNumericPrecision(), column.getNumericScale(), dblinkType); + } + + /** + * 根据数据库链接的类型,将数据表字段类型转换为Java的字段属性类型。 + * + * @param columnType 表字段类型。 + * @param numericPrecision 数值的精度。 + * @param numericScale 数值的刻度。 + * @param dblinkType 数据库链接类型。 + * @return 返回与Java字段属性的类型名。 + */ + public String convertToJavaType(String columnType, Integer numericPrecision, Integer numericScale, int dblinkType) { + DataSourceProvider provider = this.getProvider(dblinkType); + if (provider == null) { + throw new MyRuntimeException("Unsupported Data Type"); + } + return provider.convertColumnTypeToJavaType(columnType, numericPrecision, numericScale); + } + + /** + * 根据Java字段属性的类型,转换参数中的values值到与fieldType匹配的值类型数据列表。 + * + * @param fieldType Java字段属性值。 + * @param values 字符串类型的参数值列表,该参数为JSON数组。 + * @return 目标类型的参数值列表。 + */ + public List convertToColumnValues(String fieldType, String values) { + List valueList = new LinkedList<>(); + if (StrUtil.isBlank(values)) { + return valueList; + } + JSONArray valueArray = JSON.parseArray(values); + for (int i = 0; i < valueArray.size(); i++) { + String v = valueArray.getString(i); + valueList.add(this.convertToColumnValue(fieldType, v)); + } + return valueList; + } + + /** + * 根据Java字段属性的类型,转换参数中的value值到与fieldType匹配的值类型。 + * + * @param fieldType Java字段属性值。 + * @param value 参数值。 + * @return 目标类型的参数值。 + */ + public Serializable convertToColumnValue(String fieldType, Serializable value) { + if (value == null) { + return null; + } + switch (fieldType) { + case "Long": + return Convert.toLong(value); + case "Integer": + return Convert.toInt(value); + case "BigDecimal": + return Convert.toBigDecimal(value); + case "Double": + return Convert.toDouble(value); + case "Boolean": + return Convert.toBool(value); + case "Date": + return value; + case "String": + return value.toString(); + default: + break; + } + return null; + } + /** * 计算过滤从句和过滤参数。 * diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/pom.xml b/OrangeFormsOpen-MybatisPlus/common/common-flow/pom.xml index daad1d91..6dc2108f 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/pom.xml +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/pom.xml @@ -20,6 +20,11 @@ common-satoken 1.0.0 + + com.orangeforms + common-dbutil + 1.0.0 + com.orangeforms common-datafilter diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/constant/FlowApprovalType.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/constant/FlowApprovalType.java index 3ab18e1a..2d8b65df 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/constant/FlowApprovalType.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/constant/FlowApprovalType.java @@ -96,6 +96,10 @@ public final class FlowApprovalType { * 空审批人自动退回。 */ public static final String EMPTY_USER_AUTO_REJECT = "empty_user_auto_reject"; + /** + * 自动化任务。 + */ + public static final String AUTO_FLOW_TASK = "auto_flow_task"; /** * 私有构造函数,明确标识该常量类的作用。 diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/constant/FlowAutoActionType.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/constant/FlowAutoActionType.java new file mode 100644 index 00000000..05b3f8d5 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/constant/FlowAutoActionType.java @@ -0,0 +1,69 @@ +package com.orangeforms.common.flow.constant; + +import java.util.HashMap; +import java.util.Map; + +/** + * 工作流自动化任务的动作类型。 + * + * @author Jerry + * @date 2024-07-02 + */ +public class FlowAutoActionType { + + /** + * 添加新数据。 + */ + public static final int ADD_NEW = 0; + /** + * 更新数据。 + */ + public static final int UPDATE = 1; + /** + * 删除数据。 + */ + public static final int DELETE = 2; + /** + * 查询单条数据。 + */ + public static final int SELECT_ONE = 3; + /** + * 聚合计算。 + */ + public static final int AGGREGATION_CALC = 5; + /** + * 数值计算。 + */ + public static final int NUMBER_CALC = 6; + /** + * HTTP请求调用 + */ + public static final int HTTP = 10; + + private static final Map DICT_MAP = new HashMap<>(2); + static { + DICT_MAP.put(ADD_NEW, "添加新数据"); + DICT_MAP.put(UPDATE, "更新数据"); + DICT_MAP.put(DELETE, "删除数据"); + DICT_MAP.put(SELECT_ONE, "查询单条数据"); + DICT_MAP.put(AGGREGATION_CALC, "聚合计算"); + DICT_MAP.put(NUMBER_CALC, "数值计算"); + DICT_MAP.put(HTTP, "HTTP请求调用"); + } + + /** + * 根据类型值返回显示值。 + * + * @param flowActionType 类型值。 + * @return 对应的显示名。 + */ + public static String getShowNname(int flowActionType) { + return DICT_MAP.get(flowActionType); + } + + /** + * 私有构造函数,明确标识该常量类的作用。 + */ + private FlowAutoActionType() { + } +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/constant/FlowConstant.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/constant/FlowConstant.java index 12ccf122..3091f83e 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/constant/FlowConstant.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/constant/FlowConstant.java @@ -153,6 +153,11 @@ public class FlowConstant { */ public static final String GROUP_TYPE_DEPT_POST_LEADER = "DEPT_POST_LEADER"; + /** + * 自动执行。 + */ + public static final String GROUP_TYPE_AUTO_EXEC = "AUTO_EXEC"; + /** * 本部门岗位前缀。 */ @@ -258,6 +263,11 @@ public class FlowConstant { */ public static final String EMPTY_USER_TO_ASSIGNEE = "emptyUserToAssignee"; + /** + * 自动化任务中用于传递的生产者日志对象变量。 + */ + public static final String AUTO_FLOW_TRANS_PRODUCER_VAR = "transProducer"; + /** * 私有构造函数,明确标识该常量类的作用。 */ diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/controller/FlowDblinkController.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/controller/FlowDblinkController.java new file mode 100644 index 00000000..c10f8ff3 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/controller/FlowDblinkController.java @@ -0,0 +1,276 @@ +package com.orangeforms.common.flow.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.github.pagehelper.page.PageMethod; +import com.orangeforms.common.core.annotation.MyRequestBody; +import com.orangeforms.common.core.constant.ErrorCodeEnum; +import com.orangeforms.common.core.object.*; +import com.orangeforms.common.core.util.MyCommonUtil; +import com.orangeforms.common.core.util.MyModelUtil; +import com.orangeforms.common.core.util.MyPageUtil; +import com.orangeforms.common.dbutil.object.SqlTable; +import com.orangeforms.common.dbutil.object.SqlTableColumn; +import com.orangeforms.common.flow.dto.FlowDblinkDto; +import com.orangeforms.common.flow.model.FlowDblink; +import com.orangeforms.common.flow.service.FlowDblinkService; +import com.orangeforms.common.flow.util.FlowDataSourceUtil; +import com.orangeforms.common.flow.vo.FlowDblinkVo; +import com.orangeforms.common.log.annotation.OperationLog; +import com.orangeforms.common.log.model.constant.SysOperationLogType; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 工作流数据库链接接口。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Tag(name = "工作流数据库链接接口") +@Slf4j +@RestController +@RequestMapping("${common-flow.urlPrefix}/flowDblink") +@ConditionalOnProperty(name = "common-flow.operationEnabled", havingValue = "true") +public class FlowDblinkController { + + @Autowired + private FlowDblinkService flowDblinkService; + @Autowired + private FlowDataSourceUtil dataSourceUtil; + + /** + * 新增数据库链接数据。 + * + * @param flowDblinkDto 新增对象。 + * @return 应答结果对象,包含新增对象主键Id。 + */ + @SaCheckPermission("flowDblink.all") + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add(@MyRequestBody FlowDblinkDto flowDblinkDto) { + String errorMessage = MyCommonUtil.getModelValidationError(flowDblinkDto, false); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + FlowDblink flowDblink = MyModelUtil.copyTo(flowDblinkDto, FlowDblink.class); + flowDblink = flowDblinkService.saveNew(flowDblink); + return ResponseResult.success(flowDblink.getDblinkId()); + } + + /** + * 更新数据库链接数据。 + * + * @param flowDblinkDto 更新对象。 + * @return 应答结果对象。 + */ + @SaCheckPermission("flowDblink.all") + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update(@MyRequestBody FlowDblinkDto flowDblinkDto) { + String errorMessage = MyCommonUtil.getModelValidationError(flowDblinkDto, true); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + FlowDblink flowDblink = MyModelUtil.copyTo(flowDblinkDto, FlowDblink.class); + ResponseResult verifyResult = this.doVerifyAndGet(flowDblinkDto.getDblinkId()); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + FlowDblink originalFlowDblink = verifyResult.getData(); + if (ObjectUtil.notEqual(flowDblink.getDblinkType(), originalFlowDblink.getDblinkType())) { + errorMessage = "数据验证失败,不能修改数据库类型!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + String passwdKey = "password"; + JSONObject configJson = JSON.parseObject(flowDblink.getConfiguration()); + String password = configJson.getString(passwdKey); + if (StrUtil.isNotBlank(password) && StrUtil.isAllCharMatch(password, c -> '*' == c)) { + password = JSON.parseObject(originalFlowDblink.getConfiguration()).getString(passwdKey); + configJson.put(passwdKey, password); + flowDblink.setConfiguration(configJson.toJSONString()); + } + if (!flowDblinkService.update(flowDblink, originalFlowDblink)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 删除数据库链接数据。 + * + * @param dblinkId 删除对象主键Id。 + * @return 应答结果对象。 + */ + @SaCheckPermission("flowDblink.all") + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long dblinkId) { + String errorMessage; + // 验证关联Id的数据合法性 + ResponseResult verifyResult = this.doVerifyAndGet(dblinkId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + if (!flowDblinkService.remove(dblinkId)) { + errorMessage = "数据操作失败,删除的对象不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 列出符合过滤条件的数据库链接列表。 + * + * @param flowDblinkDtoFilter 过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含查询结果集。 + */ + @SaCheckPermission("flowDblink.all") + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody FlowDblinkDto flowDblinkDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + FlowDblink flowDblinkFilter = MyModelUtil.copyTo(flowDblinkDtoFilter, FlowDblink.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, FlowDblink.class); + List flowDblinkList = + flowDblinkService.getFlowDblinkListWithRelation(flowDblinkFilter, orderBy); + for (FlowDblink dblink : flowDblinkList) { + this.maskOffPassword(dblink); + } + return ResponseResult.success(MyPageUtil.makeResponseData(flowDblinkList, FlowDblinkVo.class)); + } + + /** + * 查看指定数据库链接对象详情。 + * + * @param dblinkId 指定对象主键Id。 + * @return 应答结果对象,包含对象详情。 + */ + @SaCheckPermission("flowDblink.all") + @GetMapping("/view") + public ResponseResult view(@RequestParam Long dblinkId) { + ResponseResult verifyResult = this.doVerifyAndGet(dblinkId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + FlowDblink flowDblink = verifyResult.getData(); + flowDblinkService.buildRelationForData(flowDblink, MyRelationParam.full()); + if (!StrUtil.equals(flowDblink.getAppCode(), TokenData.takeFromRequest().getAppCode())) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, "数据验证失败,当前应用并不存在该数据库链接!"); + } + this.maskOffPassword(flowDblink); + return ResponseResult.success(flowDblink, FlowDblinkVo.class); + } + + /** + * 获取指定数据库链接下的所有动态表单依赖的数据表列表。 + * + * @param dblinkId 数据库链接Id。 + * @return 所有动态表单依赖的数据表列表 + */ + @SaCheckPermission("flowDblink.all") + @GetMapping("/listDblinkTables") + public ResponseResult> listDblinkTables(@RequestParam Long dblinkId) { + FlowDblink dblink = flowDblinkService.getById(dblinkId); + if (dblink == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(flowDblinkService.getDblinkTableList(dblink)); + } + + /** + * 获取指定数据库链接下,指定数据表的所有字段信息。 + * + * @param dblinkId 数据库链接Id。 + * @param tableName 表名。 + * @return 该表的所有字段列表。 + */ + @SaCheckPermission("flowDblink.all") + @GetMapping("/listDblinkTableColumns") + public ResponseResult> listDblinkTableColumns( + @RequestParam Long dblinkId, @RequestParam String tableName) { + FlowDblink dblink = flowDblinkService.getById(dblinkId); + if (dblink == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(flowDblinkService.getDblinkTableColumnList(dblink, tableName)); + } + + /** + * 测试数据库链接的接口。 + * + * @return 应答结果。 + */ + @GetMapping("/testConnection") + public ResponseResult testConnection(@RequestParam Long dblinkId) { + ResponseResult verifyAndGet = this.doVerifyAndGet(dblinkId); + if (!verifyAndGet.isSuccess()) { + return ResponseResult.errorFrom(verifyAndGet); + } + try { + dataSourceUtil.testConnection(dblinkId); + return ResponseResult.success(); + } catch (Exception e) { + log.error("Failed to test connection with FLOW_DBLINK_ID [" + dblinkId + "]!", e); + return ResponseResult.error(ErrorCodeEnum.DATA_ACCESS_FAILED, "数据库连接失败!"); + } + } + + /** + * 以字典形式返回全部数据库链接数据集合。字典的键值为[dblinkId, dblinkName]。 + * 白名单接口,登录用户均可访问。 + * + * @param filter 过滤对象。 + * @return 应答结果对象,包含的数据为 List>,map中包含两条记录,key的值分别是id和name,value对应具体数据。 + */ + @GetMapping("/listDict") + public ResponseResult>> listDict(@ParameterObject FlowDblinkDto filter) { + List resultList = + flowDblinkService.getFlowDblinkList(MyModelUtil.copyTo(filter, FlowDblink.class), null); + return ResponseResult.success( + MyCommonUtil.toDictDataList(resultList, FlowDblink::getDblinkId, FlowDblink::getDblinkName)); + } + + private ResponseResult doVerifyAndGet(Long dblinkId) { + if (MyCommonUtil.existBlankArgument(dblinkId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + FlowDblink flowDblink = flowDblinkService.getById(dblinkId); + if (flowDblink == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + if (!StrUtil.equals(flowDblink.getAppCode(), TokenData.takeFromRequest().getAppCode())) { + return ResponseResult.error( + ErrorCodeEnum.DATA_VALIDATED_FAILED, "数据验证失败,当前应用并不存在该数据库链接!"); + } + return ResponseResult.success(flowDblink); + } + + private void maskOffPassword(FlowDblink dblink) { + String passwdKey = "password"; + JSONObject configJson = JSON.parseObject(dblink.getConfiguration()); + if (configJson.containsKey(passwdKey)) { + String password = configJson.getString(passwdKey); + if (StrUtil.isNotBlank(password)) { + configJson.put(passwdKey, StrUtil.repeat('*', password.length())); + dblink.setConfiguration(configJson.toJSONString()); + } + } + } +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/controller/FlowOperationController.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/controller/FlowOperationController.java index 73302281..8dce81bf 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/controller/FlowOperationController.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/controller/FlowOperationController.java @@ -11,6 +11,7 @@ import com.alibaba.fastjson.JSONObject; import com.orangeforms.common.core.annotation.DisableDataFilter; import com.orangeforms.common.core.annotation.MyRequestBody; import com.orangeforms.common.core.constant.ErrorCodeEnum; +import com.orangeforms.common.core.exception.MyRuntimeException; import com.orangeforms.common.core.object.*; import com.orangeforms.common.core.util.MyModelUtil; import com.orangeforms.common.core.util.MyPageUtil; @@ -35,6 +36,7 @@ import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.history.HistoricActivityInstance; import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.flowable.task.api.TaskInfo; import org.flowable.task.api.history.HistoricTaskInstance; @@ -113,6 +115,26 @@ public class FlowOperationController { return ResponseResult.success(); } + /** + * 发起一个自动化流程实例。 + * + * @param processDefinitionKey 流程标识。 + * @param variableData 变量数据。 + * @return 应答结果对象。 + */ + @SaCheckPermission("flowOperation.all") + @OperationLog(type = SysOperationLogType.START_FLOW) + @PostMapping("/startAuto") + public ResponseResult startAuto( + @MyRequestBody(required = true) String processDefinitionKey, @MyRequestBody JSONObject variableData) { + try { + ProcessInstance processInstance = flowApiService.startAuto(processDefinitionKey, variableData); + return ResponseResult.success(processInstance.getProcessInstanceId()); + } catch (MyRuntimeException e) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, e.getMessage()); + } + } + /** * 获取开始节点之后的第一个任务节点的数据。 * diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/FlowAutoVariableLogMapper.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/FlowAutoVariableLogMapper.java new file mode 100644 index 00000000..8f963e40 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/FlowAutoVariableLogMapper.java @@ -0,0 +1,13 @@ +package com.orangeforms.common.flow.dao; + +import com.orangeforms.common.core.base.dao.BaseDaoMapper; +import com.orangeforms.common.flow.model.FlowAutoVariableLog; + +/** + * 自动化流程变量访问接口。 + * + * @author Jerry + * @date 2024-07-02 + */ +public interface FlowAutoVariableLogMapper extends BaseDaoMapper { +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/FlowDblinkMapper.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/FlowDblinkMapper.java new file mode 100644 index 00000000..f9c4cdb3 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/FlowDblinkMapper.java @@ -0,0 +1,26 @@ +package com.orangeforms.common.flow.dao; + +import com.orangeforms.common.core.base.dao.BaseDaoMapper; +import com.orangeforms.common.flow.model.FlowDblink; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 数据库链接数据操作访问接口。 + * + * @author Jerry + * @date 2024-07-02 + */ +public interface FlowDblinkMapper extends BaseDaoMapper { + + /** + * 获取过滤后的对象列表。 + * + * @param flowDblinkFilter 主表过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 对象列表。 + */ + List getFlowDblinkList( + @Param("flowDblinkFilter") FlowDblink flowDblinkFilter, @Param("orderBy") String orderBy); +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/FlowTransProducerMapper.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/FlowTransProducerMapper.java new file mode 100644 index 00000000..9594086c --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/FlowTransProducerMapper.java @@ -0,0 +1,13 @@ +package com.orangeforms.common.flow.dao; + +import com.orangeforms.common.core.base.dao.BaseDaoMapper; +import com.orangeforms.common.flow.model.FlowTransProducer; + +/** + * 事务性业务数据生产者访问接口。 + * + * @author Jerry + * @date 2024-07-02 + */ +public interface FlowTransProducerMapper extends BaseDaoMapper { +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowAutoVariableLogMapper.xml b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowAutoVariableLogMapper.xml new file mode 100644 index 00000000..eb72a86f --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowAutoVariableLogMapper.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowDblinkMapper.xml b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowDblinkMapper.xml new file mode 100644 index 00000000..99ebf0f6 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowDblinkMapper.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + AND zz_flow_dblink.app_code IS NULL + + + AND zz_flow_dblink.app_code = #{flowDblinkFilter.appCode} + + + AND zz_flow_dblink.dblink_type = #{flowDblinkFilter.dblinkType} + + + + + + diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowEntryMapper.xml b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowEntryMapper.xml index 78351d5d..abd7f92e 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowEntryMapper.xml +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowEntryMapper.xml @@ -14,6 +14,7 @@ + @@ -58,6 +59,9 @@ AND zz_flow_entry.status = #{flowEntryFilter.status} + + AND zz_flow_entry.flow_type = #{flowEntryFilter.flowType} + @@ -73,6 +77,7 @@ status, diagram_type, bind_form_type, + flow_type, page_id, default_form_id, default_router_name, diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowTaskExtMapper.xml b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowTaskExtMapper.xml index 2fca8da4..edccb484 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowTaskExtMapper.xml +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowTaskExtMapper.xml @@ -14,6 +14,7 @@ + @@ -30,7 +31,8 @@ #{item.deptIds}, #{item.candidateUsernames}, #{item.copyListJson}, - #{item.extraDataJson}) + #{item.extraDataJson}, + #{item.autoConfigJson}) diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowTransProducerMapper.xml b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowTransProducerMapper.xml new file mode 100644 index 00000000..87445227 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dao/mapper/FlowTransProducerMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dto/FlowDblinkDto.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dto/FlowDblinkDto.java new file mode 100644 index 00000000..c394744e --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/dto/FlowDblinkDto.java @@ -0,0 +1,53 @@ +package com.orangeforms.common.flow.dto; + +import com.orangeforms.common.core.validator.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +/** + * 工作流自动化流程数据表所在数据库链接Dto对象。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Schema(description = "工作流自动化流程数据表所在数据库链接Dto对象") +@Data +public class FlowDblinkDto { + + /** + * 主键Id。 + */ + @Schema(description = "主键Id") + @NotNull(message = "数据验证失败,主键Id不能为空!", groups = {UpdateGroup.class}) + private Long dblinkId; + + /** + * 链接中文名称。 + */ + @Schema(description = "链接中文名称") + @NotBlank(message = "数据验证失败,链接中文名称不能为空!") + private String dblinkName; + + /** + * 链接描述。 + */ + @Schema(description = "链接中文名称") + private String dblinkDescription; + + /** + * 配置信息。 + */ + @Schema(description = "配置信息") + @NotBlank(message = "数据验证失败,配置信息不能为空!") + private String configuration; + + /** + * 数据库链接类型。 + */ + @Schema(description = "数据库链接类型") + @NotNull(message = "数据验证失败,数据库链接类型不能为空!") + private Integer dblinkType; +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/listener/ReceiveTaskEndListener.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/listener/ReceiveTaskEndListener.java new file mode 100644 index 00000000..eff45b3f --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/listener/ReceiveTaskEndListener.java @@ -0,0 +1,86 @@ +package com.orangeforms.common.flow.listener; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import com.orangeforms.common.core.util.ApplicationContextHolder; +import com.orangeforms.common.flow.constant.FlowApprovalType; +import com.orangeforms.common.flow.constant.FlowAutoActionType; +import com.orangeforms.common.flow.constant.FlowConstant; +import com.orangeforms.common.flow.model.FlowTaskComment; +import com.orangeforms.common.flow.model.FlowTransProducer; +import com.orangeforms.common.flow.object.AutoTaskConfig; +import com.orangeforms.common.flow.service.FlowApiService; +import com.orangeforms.common.flow.service.FlowTaskCommentService; +import com.orangeforms.common.flow.service.FlowTransProducerService; +import com.orangeforms.common.flow.util.AutoFlowHelper; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.ExecutionListener; + +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 空审批人审批人检测监听器。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Slf4j +public class ReceiveTaskEndListener implements ExecutionListener { + + private final transient FlowApiService flowApiService = + ApplicationContextHolder.getBean(FlowApiService.class); + private final transient FlowTransProducerService flowTransProducerService = + ApplicationContextHolder.getBean(FlowTransProducerService.class); + private final transient FlowTaskCommentService flowTaskCommentService = + ApplicationContextHolder.getBean(FlowTaskCommentService.class); + private final transient AutoFlowHelper autoFlowHelper = + ApplicationContextHolder.getBean(AutoFlowHelper.class); + + @Override + public void notify(DelegateExecution d) { + //先从内存中的临时变量中获取,以便提升正常运行情况下的运行时效率。 + FlowTransProducer transProducer = + (FlowTransProducer) d.getTransientVariable(FlowConstant.AUTO_FLOW_TRANS_PRODUCER_VAR); + //如果临时变量中没有存在,则从流程执行实例中查询该变量。 + if (transProducer == null) { + transProducer = (FlowTransProducer) + flowApiService.getExecutionVariable(d.getId(), FlowConstant.AUTO_FLOW_TRANS_PRODUCER_VAR); + } + if (transProducer == null) { + FlowTransProducer filter = new FlowTransProducer(); + filter.setProcessInstanceId(d.getProcessInstanceId()); + filter.setExecutionId(d.getId()); + filter.setTaskKey(d.getCurrentActivityId()); + List transProducers = flowTransProducerService.getListByFilter(filter); + if (CollUtil.isNotEmpty(transProducers)) { + transProducers = transProducers.stream() + .sorted(Comparator.comparing(FlowTransProducer::getTransId, Comparator.reverseOrder())) + .collect(Collectors.toList()); + transProducer = transProducers.get(0); + } + } + if (transProducer == null) { + return; + } + if (StrUtil.isNotBlank(transProducer.getAutoTaskConfig())) { + AutoTaskConfig taskConfig = JSON.parseObject(transProducer.getAutoTaskConfig(), AutoTaskConfig.class); + autoFlowHelper.executeTask(transProducer.getTransId(), taskConfig, d); + FlowTaskComment comment = new FlowTaskComment(); + comment.setTaskKey(d.getCurrentActivityId()); + comment.setTaskName(taskConfig.getTaskName()); + comment.setProcessInstanceId(d.getProcessInstanceId()); + comment.setExecutionId(d.getId()); + comment.setApprovalType(FlowApprovalType.AUTO_FLOW_TASK); + String s = StrFormatter.format("执行任务 [{}] 成功,任务类型 [{}]", + taskConfig.getTaskName(), FlowAutoActionType.getShowNname(taskConfig.getActionType())); + comment.setTaskComment(s); + flowTaskCommentService.saveNew(comment); + } + flowTransProducerService.removeById(transProducer.getTransId()); + } +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/listener/ReceiveTaskStartListener.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/listener/ReceiveTaskStartListener.java new file mode 100644 index 00000000..87809cf4 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/listener/ReceiveTaskStartListener.java @@ -0,0 +1,46 @@ +package com.orangeforms.common.flow.listener; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.orangeforms.common.core.util.ApplicationContextHolder; +import com.orangeforms.common.flow.model.FlowTransProducer; +import com.orangeforms.common.flow.object.AutoTaskConfig; +import com.orangeforms.common.flow.service.FlowTransProducerService; +import com.orangeforms.common.flow.util.AutoFlowHelper; +import com.orangeforms.common.flow.util.ListenerEventPublishHelper; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.ExecutionListener; + +/** + * 空审批人审批人检测监听器。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Slf4j +public class ReceiveTaskStartListener implements ExecutionListener { + + private final transient AutoFlowHelper autoFlowHelper = + ApplicationContextHolder.getBean(AutoFlowHelper.class); + private final transient FlowTransProducerService flowTransProducerService = + ApplicationContextHolder.getBean(FlowTransProducerService.class); + private final transient ListenerEventPublishHelper eventPublishHelper = + ApplicationContextHolder.getBean(ListenerEventPublishHelper.class); + + @Override + public void notify(DelegateExecution d) { + //TODO 在目标表所在数据库也要创建业务执行的流水表,基于TransProduer的TransId做唯一性校验。 + AutoTaskConfig taskConfig = autoFlowHelper.parseAutoTaskConfig(d.getProcessDefinitionId(), d.getCurrentActivityId()); + FlowTransProducer producerData = new FlowTransProducer(); + producerData.setProcessInstanceId(d.getProcessInstanceId()); + producerData.setExecutionId(d.getId()); + producerData.setTaskKey(d.getCurrentActivityId()); + producerData.setDblinkId(taskConfig.getDestDblinkId()); + producerData.setInitMethod("ReceiveTaskStartListener.notify"); + producerData.setTryTimes(1); + producerData.setAutoTaskConfig(JSON.toJSONString(taskConfig, SerializerFeature.WriteDateUseDateFormat)); + flowTransProducerService.saveNew(producerData); + eventPublishHelper.publishEvent(producerData); + } +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowAutoVariableLog.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowAutoVariableLog.java new file mode 100644 index 00000000..95d4e62a --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowAutoVariableLog.java @@ -0,0 +1,67 @@ +package com.orangeforms.common.flow.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +/** + * 自动化流程变量实体。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Data +@TableName(value = "zz_flow_auto_variable_log") +public class FlowAutoVariableLog { + + /** + * 主键Id。 + */ + @TableId(value = "id") + private Long id; + + /** + * 流程实例Id。 + */ + @TableField(value = "process_instance_id") + private String processInstanceId; + + /** + * 执行实例Id。 + */ + @TableField(value = "execution_id") + private String executionId; + + /** + * 任务Id。 + */ + @TableField(value = "task_id") + private String taskId; + + /** + * 任务标识。 + */ + @TableField(value = "task_key") + private String taskKey; + + /** + * 当前请求的traceId。 + */ + @TableField(value = "trace_id") + private String traceId; + + /** + * 变量数据。 + */ + @TableField(value = "variable_data") + private String variableData; + + /** + * 创建时间。 + */ + @TableField(value = "create_time") + private Date createTime; +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowDblink.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowDblink.java new file mode 100644 index 00000000..9fc64650 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowDblink.java @@ -0,0 +1,87 @@ +package com.orangeforms.common.flow.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.orangeforms.common.core.annotation.RelationConstDict; +import com.orangeforms.common.dbutil.constant.DblinkType; +import lombok.Data; + +import java.util.Date; +import java.util.Map; + +/** + * 在线表单数据表所在数据库链接实体对象。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Data +@TableName(value = "zz_flow_dblink") +public class FlowDblink { + + /** + * 主键Id。 + */ + @TableId(value = "dblink_id") + private Long dblinkId; + + /** + * 应用编码。为空时,表示非第三方应用接入。 + */ + @TableField(value = "app_code") + private String appCode; + + /** + * 链接中文名称。 + */ + @TableField(value = "dblink_name") + private String dblinkName; + + /** + * 链接描述。 + */ + @TableField(value = "dblink_description") + private String dblinkDescription; + + /** + * 配置信息。 + */ + private String configuration; + + /** + * 数据库链接类型。 + */ + @TableField(value = "dblink_type") + private Integer dblinkType; + + /** + * 创建时间。 + */ + @TableField(value = "create_time") + private Date createTime; + + /** + * 创建者。 + */ + @TableField(value = "create_user_id") + private Long createUserId; + + /** + * 修改时间。 + */ + @TableField(value = "update_time") + private Date updateTime; + + /** + * 更新者。 + */ + @TableField(value = "update_user_id") + private Long updateUserId; + + @RelationConstDict( + masterIdField = "dblinkType", + constantDictClass = DblinkType.class) + @TableField(exist = false) + private Map dblinkTypeDictMap; +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowEntry.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowEntry.java index 6510c1c6..3c8beb27 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowEntry.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowEntry.java @@ -88,6 +88,12 @@ public class FlowEntry { @TableField(value = "bind_form_type") private Integer bindFormType; + /** + * 流程类型。 + */ + @TableField(value = "flow_type") + private Integer flowType; + /** * 在线表单的页面Id。 */ diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowEntryVariable.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowEntryVariable.java index bbb8df66..1ba55bd8 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowEntryVariable.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowEntryVariable.java @@ -1,6 +1,7 @@ package com.orangeforms.common.flow.model; import com.baomidou.mybatisplus.annotation.*; +import com.orangeforms.common.flow.model.constant.FlowVariableType; import lombok.Data; import java.util.Date; @@ -74,4 +75,13 @@ public class FlowEntryVariable { */ @TableField(value = "create_time") private Date createTime; + + public static FlowEntryVariable createSystemVariable(String variableName, String showName) { + FlowEntryVariable variable = new FlowEntryVariable(); + variable.variableName = variableName; + variable.showName = showName; + variable.variableType = FlowVariableType.SYSTEM; + variable.builtin = true; + return variable; + } } diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowTaskExt.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowTaskExt.java index 725c9ac0..8a7441f4 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowTaskExt.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowTaskExt.java @@ -84,4 +84,10 @@ public class FlowTaskExt { */ @TableField(value = "extra_data_json") private String extraDataJson; + + /** + * 自动化任务配置数据,存储为JSON的字符串格式。 + */ + @TableField(value = "auto_config_json") + private String autoConfigJson; } diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowTransProducer.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowTransProducer.java new file mode 100644 index 00000000..3675b14e --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/FlowTransProducer.java @@ -0,0 +1,133 @@ +package com.orangeforms.common.flow.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +/** + * 流程处理事务事件生产者流水实体。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Data +@TableName(value = "zz_flow_trans_producer") +public class FlowTransProducer { + + /** + * 流水Id。 + */ + @TableId(value = "trans_id") + private Long transId; + + /** + * 应用编码。为空时,表示非第三方应用接入。 + */ + @TableField(value = "app_code") + private String appCode; + + /** + * 业务数据库链接Id。 + */ + @TableField(value = "dblink_id") + private Long dblinkId; + + /** + * 流程实例Id。 + */ + @TableField(value = "process_instance_id") + private String processInstanceId; + + /** + * 执行实例Id。 + */ + @TableField(value = "execution_id") + private String executionId; + + /** + * 任务Id。 + */ + @TableField(value = "task_id") + private String taskId; + + /** + * 任务标识。 + */ + @TableField(value = "task_key") + private String taskKey; + + /** + * 任务名称。 + */ + @TableField(value = "task_name") + private String taskName; + + /** + * 审批批注。 + */ + @TableField(value = "task_comment") + private String taskComment; + + /** + * 当前请求的url。 + */ + @TableField(value = "url") + private String url; + + /** + * 创建该事务性事件对象的初始方法。格式为:方法名(参数类型1,参数类型2)。 + */ + @TableField(value = "init_method") + private String initMethod; + + /** + * 当前请求的traceId。 + */ + @TableField(value = "trace_id") + private String traceId; + + /** + * 和SQL操作相关的数据。值类型为TransactionalBusinessData.BusinessSqlData对象。 + */ + @TableField(value = "sql_data") + private String sqlData; + + /** + * 自动化流程的任务配置。 + */ + @TableField(value = "auto_task_config") + private String autoTaskConfig; + + /** + * 尝试次数。默认的插入值为1。 + */ + @TableField(value = "try_times") + private Integer tryTimes; + + /** + * 提交业务数据时的错误信息。如果正常提交,该值为空。 + */ + @TableField(value = "error_reason") + private String errorReason; + + /** + * 创建者登录名。 + */ + @TableField(value = "create_login_name") + private String createLoginName; + + /** + * 创建者中文用户名。 + */ + @TableField(value = "create_username") + private String createUsername; + + /** + * 创建时间。 + */ + @TableField(value = "create_time") + private Date createTime; +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/constant/FlowEntryType.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/constant/FlowEntryType.java new file mode 100644 index 00000000..9030037e --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/constant/FlowEntryType.java @@ -0,0 +1,44 @@ +package com.orangeforms.common.flow.model.constant; + +import java.util.HashMap; +import java.util.Map; + +/** + * 流程类型。 + * + * @author Jerry + * @date 2024-07-02 + */ +public final class FlowEntryType { + + /** + * 普通审批。 + */ + public static final int NORMAL_TYPE = 0; + /** + * 自动化流程。 + */ + public static final int AUTO_TYPE = 1; + + private static final Map DICT_MAP = new HashMap<>(2); + static { + DICT_MAP.put(NORMAL_TYPE, "普通审批"); + DICT_MAP.put(AUTO_TYPE, "自动化流程"); + } + + /** + * 判断参数是否为当前常量字典的合法值。 + * + * @param value 待验证的参数值。 + * @return 合法返回true,否则false。 + */ + public static boolean isValid(Integer value) { + return value != null && DICT_MAP.containsKey(value); + } + + /** + * 私有构造函数,明确标识该常量类的作用。 + */ + private FlowEntryType() { + } +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/constant/FlowVariableType.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/constant/FlowVariableType.java index f68f49ad..af9ce8e0 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/constant/FlowVariableType.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/model/constant/FlowVariableType.java @@ -19,11 +19,21 @@ public final class FlowVariableType { * 任务变量。 */ public static final int TASK = 1; + /** + * 系统内置变量。 + */ + public static final int SYSTEM = 2; + /** + * 自定义变量。 + */ + public static final int CUSTOM = 4; private static final Map DICT_MAP = new HashMap<>(2); static { DICT_MAP.put(INSTANCE, "流程实例变量"); DICT_MAP.put(TASK, "任务变量"); + DICT_MAP.put(SYSTEM, "系统内置变量"); + DICT_MAP.put(CUSTOM, "自定义变量"); } /** diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoExecData.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoExecData.java new file mode 100644 index 00000000..0ee0435d --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoExecData.java @@ -0,0 +1,28 @@ +package com.orangeforms.common.flow.object; + +import lombok.Data; + +import java.util.List; + +/** + * 自动化任务的执行数据。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Data +public class AutoExecData { + + /** + * 数据库链接ID。 + */ + private Long dblinkId; + /** + * 执行sql。 + */ + private String sql; + /** + * sql参数列表。 + */ + private List params; +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoHttpRequestInfo.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoHttpRequestInfo.java new file mode 100644 index 00000000..7ac11630 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoHttpRequestInfo.java @@ -0,0 +1,53 @@ +package com.orangeforms.common.flow.object; + +import lombok.Data; + +import java.util.List; + +/** + * 自动化任务调用HTTP请求的对象。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Data +public class AutoHttpRequestInfo { + + public static final String BODY_TYPE_FORMDATA = "formData"; + public static final String BODY_TYPE_RAW = "raw"; + public static final String RAW_TYPE_TEXT = "text"; + public static final String RAW_TYPE_JSON = "json"; + + /** + * 请求地址。 + */ + private String url; + /** + * POST/GET/PUT ... + */ + private String httpMethod; + /** + * 请求body的类型。 + */ + private String bodyType; + /** + * 仅当bodyType为raw的时候可用。 + */ + private String rawType; + /** + * 当bodyType为raw的时候,请求体的数据。 + */ + private String bodyData; + /** + * HTTP请求头列表。 + */ + private List headerList; + /** + * 仅当bodyType为formData时可用。 + */ + private List formDataList; + /** + * url参数。 + */ + private List urlParamList; +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoHttpResponseData.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoHttpResponseData.java new file mode 100644 index 00000000..2574630c --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoHttpResponseData.java @@ -0,0 +1,38 @@ +package com.orangeforms.common.flow.object; + +import lombok.Data; + +/** + * 自动化任务调用HTTP的应答对象。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Data +public class AutoHttpResponseData { + + public static final String STOP_ON_FAIL = "stop"; + public static final String CONTINUE_ON_FAIL = "continue"; + + /** + * 应答成功的HTTP状态码,多个状态码之间逗号分隔。 + */ + private String successStatusCode; + /** + * 如果请求状态码为成功,还需要进一步根据应答体中的指定字段,进一步判断是否成功。 + * 如:data.isSuccess。 + */ + private String successBodyField = "success"; + /** + * 请求体中错误信息字段。 + */ + private String errorMessageBodyField = "errorMessage"; + /** + * 失败处理类型。 + */ + private String failHandleType; + /** + * HTTP请求的应答体定义。 + */ + private String httpResponseBody; +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoTaskConfig.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoTaskConfig.java new file mode 100644 index 00000000..6e6aae07 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoTaskConfig.java @@ -0,0 +1,180 @@ +package com.orangeforms.common.flow.object; + +import lombok.Data; + +import java.util.List; + +/** + * 自动化任务的执行配置数据。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Data +public class AutoTaskConfig { + + public static final String SRC_FILTER_SQL = "sql"; + public static final String SRC_FILTER_FIELD = "field"; + /** + * 固定值。 + */ + public static final int FIXED_VALUE = 0; + /** + * 表字段值。 + */ + public static final int COLUMN_VALUE = 1; + /** + * 流程变量。 + */ + public static final int VARIABLE_VALUE = 2; + + /** + * 任务标识,同时也是当前任务的输出变量名。 + */ + private String taskKey; + /** + * 任务名称。 + */ + private String taskName; + /** + * 执行动作的类型。 + */ + private Integer actionType; + /** + * 数据库链接Id。 + */ + private Long srcDblinkId; + /** + * 源数据库类型。 + */ + private Integer srcDblinkType; + /** + * 源表名称。 + */ + private String srcTableName; + /** + * 源数据的过滤类型,可能值为sql/field。 + */ + private String srcFilterType; + /** + * 源数据过滤条件列表。 + */ + private List srcFilterList; + /** + * 源数据过滤sql。 + */ + private String srcFilterSql; + /** + * 目标数据库链接Id。 + */ + private Long destDblinkId; + /** + * 目标数据库类型。 + */ + private Integer destDblinkType; + /** + * 目标表名称。 + */ + private String destTableName; + /** + * 目标数据的过滤类型,可能值为sql/field。 + */ + private String destFilterType; + /** + * 目标数据过滤条件列表。 + */ + private List destFilterList; + /** + * 目标数据过滤sql。 + */ + private String destFilterSql; + /** + * 逻辑删除字段。 + */ + private String logicDeleteField; + /** + * 数据插入对象 + */ + private List insertDataList; + /** + * 数据插入对象 + */ + private List updateDataList; + /** + * SELECT查询的字段。 + */ + private List selectFieldList; + /** + * 聚合数据列表。 + */ + private List aggregationDataList; + /** + * 数值计算表达式。 + */ + private String calculateFormula; + /** + * HTTP请求信息。 + */ + private AutoHttpRequestInfo httpRequestInfo; + /** + * HTTP应答数据。 + */ + private AutoHttpResponseData httpResponnseData; + + @Data + public static class ValueInfo { + + /** + * HTTP任务中使用。 + */ + private String key; + /** + * 目标字段名。 + */ + private String destColumnName; + /** + * 值的类型,参考当前对象的常量。 + */ + private Integer type; + /** + * 值。 + */ + private String srcValue; + } + + @Data + public static class FilterInfo { + /** + * 过滤字段名。 + */ + private String filterColumnName; + /** + * 过滤类型。 + */ + private Integer filterType; + /** + * 过滤值类型。 + */ + private Integer valueType; + /** + * 过滤值。 + */ + private String filterValue; + } + + @Data + public static class AggregationInfo { + /** + * 聚合函数,如SUM/COUNT/AVERAGE/MIN/MAX。 + */ + private String aggregationFunction; + /** + * 聚合字段。 + */ + private String aggregationColumn; + /** + * 别名。 + */ + private String alias; + } +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoTaskVariable.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoTaskVariable.java new file mode 100644 index 00000000..c1531479 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/object/AutoTaskVariable.java @@ -0,0 +1,43 @@ +package com.orangeforms.common.flow.object; + +import lombok.Data; + +import java.util.List; + +/** + * 自动化任务的变量对象。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Data +public class AutoTaskVariable { + /** + * 流程任务定义。 + */ + private String taskKey; + /** + * 流程名称。 + */ + private String taskName; + /** + * 执行动作的类型。 + */ + private Integer actionType; + /** + * 输出变量名。 + */ + private String outputVariableName; + /** + * 输出变量的显示名。 + */ + private String outputVariableShowName; + /** + * 选择的字段列表。 + */ + private List fieldList; + /** + * HTTP请求的应答体定义。 + */ + private String httpResponseBody; +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowApiService.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowApiService.java index edebe800..34552677 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowApiService.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowApiService.java @@ -22,7 +22,6 @@ import org.flowable.task.api.Task; import org.flowable.task.api.TaskInfo; import org.flowable.task.api.history.HistoricTaskInstance; -import javax.xml.stream.XMLStreamException; import java.text.ParseException; import java.util.Collection; import java.util.List; @@ -46,6 +45,15 @@ public interface FlowApiService { */ ProcessInstance start(String processDefinitionId, Object dataId); + /** + * 启动自动化流程实例。 + * + * @param processDefinitionKey 流程定义标识。 + * @param variableData 变量参数数据。 + * @return 新启动的流程实例。 + */ + ProcessInstance startAuto(String processDefinitionKey, JSONObject variableData); + /** * 完成第一个用户任务。 * @@ -515,9 +523,8 @@ public interface FlowApiService { * * @param bpmnXml xml格式的流程模型字符串。 * @return 转换后的标准的流程模型。 - * @throws XMLStreamException XML流处理异常 */ - BpmnModel convertToBpmnModel(String bpmnXml) throws XMLStreamException; + BpmnModel convertToBpmnModel(String bpmnXml); /** * 回退到上一个用户任务节点。如果没有指定,则回退到上一个任务。 @@ -558,6 +565,14 @@ public interface FlowApiService { Tuple2, Set> getDeptPostIdAndPostIds( FlowTaskExt flowTaskExt, String processInstanceId, boolean historic); + /** + * 获取指定流程实例中正在运行是的任务Id列表。 + * + * @param processInstanceId 流程实例Id。 + * @return 指定流程实例中正在运行是的任务Id列表。 + */ + List getCurrentActivityIds(String processInstanceId); + /** * 获取流程图中所有用户任务的映射。 * diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowAutoVariableLogService.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowAutoVariableLogService.java new file mode 100644 index 00000000..823cf294 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowAutoVariableLogService.java @@ -0,0 +1,28 @@ +package com.orangeforms.common.flow.service; + +import com.orangeforms.common.core.base.service.IBaseService; +import com.orangeforms.common.flow.model.FlowAutoVariableLog; + +/** + * 自动化流程变量操作服务接口。 + * + * @author Jerry + * @date 2024-07-02 + */ +public interface FlowAutoVariableLogService extends IBaseService { + + /** + * 保存数据对象。 + * + * @param flowAutoVariableLog 数据对象。 + */ + void saveNew(FlowAutoVariableLog flowAutoVariableLog); + + /** + * 获取指定自动化流程实例的最新变量对象。 + * + * @param processInstanceId 流程实例Id。 + * @return 自动化流程实例的最新变量对象。 + */ + FlowAutoVariableLog getAutoVariableByProcessInstanceId(String processInstanceId); +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowDblinkService.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowDblinkService.java new file mode 100644 index 00000000..c8f804b2 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowDblinkService.java @@ -0,0 +1,89 @@ +package com.orangeforms.common.flow.service; + +import com.orangeforms.common.core.base.service.IBaseService; +import com.orangeforms.common.dbutil.object.SqlTable; +import com.orangeforms.common.dbutil.object.SqlTableColumn; +import com.orangeforms.common.flow.model.FlowDblink; + +import java.util.List; + +/** + * 数据库链接数据操作服务接口。 + * + * @author Jerry + * @date 2024-07-02 + */ +public interface FlowDblinkService extends IBaseService { + + /** + * 保存新增对象。 + * + * @param flowDblink 新增对象。 + * @return 返回新增对象。 + */ + FlowDblink saveNew(FlowDblink flowDblink); + + /** + * 更新数据对象。 + * + * @param flowDblink 更新的对象。 + * @param originalFlowDblink 原有数据对象。 + * @return 成功返回true,否则false。 + */ + boolean update(FlowDblink flowDblink, FlowDblink originalFlowDblink); + + /** + * 删除指定数据。 + * + * @param dblinkId 主键Id。 + * @return 成功返回true,否则false。 + */ + boolean remove(Long dblinkId); + + /** + * 获取单表查询结果。由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。 + * 如果需要同时获取关联数据,请移步(getOnlineDblinkListWithRelation)方法。 + * + * @param filter 过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + List getFlowDblinkList(FlowDblink filter, String orderBy); + + /** + * 获取主表的查询结果,以及主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。 + * 该查询会涉及到一对一从表的关联过滤,或一对多从表的嵌套关联过滤,因此性能不如单表过滤。 + * 如果仅仅需要获取主表数据,请移步(getOnlineDblinkList),以便获取更好的查询性能。 + * + * @param filter 主表过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + List getFlowDblinkListWithRelation(FlowDblink filter, String orderBy); + + /** + * 获取指定DBLink下面的全部数据表。 + * + * @param dblink 数据库链接对象。 + * @return 全部数据表列表。 + */ + List getDblinkTableList(FlowDblink dblink); + + /** + * 获取指定DBLink下,指定表名的数据表对象,及其关联字段列表。 + * + * @param dblink 数据库链接对象。 + * @param tableName 数据库中的数据表名。 + * @return 数据表对象。 + */ + SqlTable getDblinkTable(FlowDblink dblink, String tableName); + + /** + * 获取指定DBLink下,指定表名的字段列表。 + * + * @param dblink 数据库链接对象。 + * @param tableName 数据库中的数据表名。 + * @return 表的字段列表。 + */ + List getDblinkTableColumnList(FlowDblink dblink, String tableName); +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowTaskExtService.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowTaskExtService.java index dca29c00..ec7149d1 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowTaskExtService.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowTaskExtService.java @@ -76,6 +76,13 @@ public interface FlowTaskExtService extends IBaseService { String executionId, FlowTaskExt flowTaskExt); + /** + * 验证自动化任务的配置,如果有问题直接抛出MyRuntimeException异常。 + * + * @param taskExt 流程任务的扩展。 + */ + void verifyAutoTaskConfig(FlowTaskExt taskExt); + /** * 通过UserTask对象中的扩展节点信息,构建FLowTaskExt对象。 * diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowTransProducerService.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowTransProducerService.java new file mode 100644 index 00000000..00c17e28 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/FlowTransProducerService.java @@ -0,0 +1,21 @@ +package com.orangeforms.common.flow.service; + +import com.orangeforms.common.core.base.service.IBaseService; +import com.orangeforms.common.flow.model.FlowTransProducer; + +/** + * 流程引擎审批操作的生产者流水服务接口。 + * + * @author Jerry + * @date 2024-07-02 + */ +public interface FlowTransProducerService extends IBaseService { + + /** + * 保存新数据。 + * + * @param data 数据对象。 + * @return 更新后的对象。 + */ + FlowTransProducer saveNew(FlowTransProducer data); +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowApiServiceImpl.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowApiServiceImpl.java index 011f82b8..6985247d 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowApiServiceImpl.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowApiServiceImpl.java @@ -25,14 +25,13 @@ import com.orangeforms.common.flow.constant.FlowConstant; import com.orangeforms.common.flow.constant.FlowTaskStatus; import com.orangeforms.common.flow.exception.FlowOperationException; import com.orangeforms.common.flow.model.*; +import com.orangeforms.common.flow.model.constant.FlowEntryType; import com.orangeforms.common.flow.object.FlowEntryExtensionData; import com.orangeforms.common.flow.object.FlowTaskMultiSignAssign; import com.orangeforms.common.flow.object.FlowTaskOperation; import com.orangeforms.common.flow.object.FlowTaskPostCandidateGroup; import com.orangeforms.common.flow.service.*; -import com.orangeforms.common.flow.util.BaseFlowIdentityExtHelper; -import com.orangeforms.common.flow.util.CustomChangeActivityStateBuilderImpl; -import com.orangeforms.common.flow.util.FlowCustomExtFactory; +import com.orangeforms.common.flow.util.*; import com.orangeforms.common.flow.vo.FlowTaskVo; import com.orangeforms.common.flow.vo.FlowUserInfoVo; import lombok.Cleanup; @@ -114,6 +113,14 @@ public class FlowApiServiceImpl implements FlowApiService { private FlowCustomExtFactory flowCustomExtFactory; @Autowired private FlowMultiInstanceTransService flowMultiInstanceTransService; + @Autowired + private FlowTransProducerService flowTransProducerService; + @Autowired + private FlowAutoVariableLogService flowAutoVariableLogService; + @Autowired + private FlowOperationHelper flowOperationHelper; + @Autowired + private AutoFlowHelper autoFlowHelper; @Transactional(rollbackFor = Exception.class) @Override @@ -134,6 +141,45 @@ public class FlowApiServiceImpl implements FlowApiService { return builder.start(); } + @Transactional(rollbackFor = Exception.class) + @Override + public ProcessInstance startAuto(String processDefinitionKey, JSONObject variableData) { + ResponseResult flowEntryResult = flowOperationHelper.verifyAndGetFlowEntry(processDefinitionKey); + if (!flowEntryResult.isSuccess()) { + throw new MyRuntimeException(flowEntryResult.getErrorMessage()); + } + FlowEntry flowEntry = flowEntryResult.getData(); + if (!flowEntry.getFlowType().equals(FlowEntryType.AUTO_TYPE)) { + throw new MyRuntimeException("数据验证失败,该接口只能启动自动化流程!"); + } + JSONObject systemVariables = autoFlowHelper.getNonRealtimeSystemVariables(); + if (variableData == null) { + variableData = new JSONObject(); + } + variableData.putAll(systemVariables); + AutoFlowHelper.setStartAutoInitVariables(variableData); + TokenData tokenData = TokenData.takeFromRequest(); + Authentication.setAuthenticatedUserId(tokenData.getLoginName()); + String businessKey = variableData.getString("businessKey"); + String processDefinitionId = flowEntry.getMainFlowEntryPublish().getProcessDefinitionId(); + ProcessInstanceBuilder builder = + runtimeService.createProcessInstanceBuilder() + .processDefinitionId(processDefinitionId).businessKey(businessKey); + if (tokenData.getTenantId() != null) { + builder.tenantId(tokenData.getTenantId().toString()); + } else { + if (tokenData.getAppCode() != null) { + builder.tenantId(tokenData.getAppCode()); + } + } + ProcessInstance instance = builder.start(); + FlowAutoVariableLog data = new FlowAutoVariableLog(); + data.setProcessInstanceId(instance.getProcessInstanceId()); + data.setVariableData(variableData.toJSONString()); + flowAutoVariableLogService.saveNew(data); + return instance; + } + @Transactional(rollbackFor = Exception.class) @Override public Task takeFirstTask(String processInstanceId, FlowTaskComment flowTaskComment, JSONObject taskVariableData) { @@ -983,6 +1029,12 @@ public class FlowApiServiceImpl implements FlowApiService { @Transactional(rollbackFor = Exception.class) @Override public CallResult stopProcessInstance(String processInstanceId, String stopReason, int status) { + ProcessInstance instance = this.getProcessInstance(processInstanceId); + FlowEntry flowEntry = flowEntryService.getFlowEntryFromCache(instance.getProcessDefinitionKey()); + if (flowEntry.getFlowType().equals(FlowEntryType.AUTO_TYPE)) { + runtimeService.deleteProcessInstance(processInstanceId, stopReason); + return CallResult.ok(); + } List taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).active().list(); if (CollUtil.isEmpty(taskList)) { return CallResult.error("数据验证失败,当前流程尚未开始或已经结束!"); @@ -1092,11 +1144,15 @@ public class FlowApiServiceImpl implements FlowApiService { } @Override - public BpmnModel convertToBpmnModel(String bpmnXml) throws XMLStreamException { - BpmnXMLConverter converter = new BpmnXMLConverter(); - InputStream in = new ByteArrayInputStream(bpmnXml.getBytes(StandardCharsets.UTF_8)); - @Cleanup XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(in); - return converter.convertToBpmnModel(reader); + public BpmnModel convertToBpmnModel(String bpmnXml) { + try { + BpmnXMLConverter converter = new BpmnXMLConverter(); + InputStream in = new ByteArrayInputStream(bpmnXml.getBytes(StandardCharsets.UTF_8)); + @Cleanup XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(in); + return converter.convertToBpmnModel(reader); + } catch (XMLStreamException e) { + throw new MyRuntimeException(e); + } } @Transactional @@ -1722,6 +1778,15 @@ public class FlowApiServiceImpl implements FlowApiService { return new Tuple2<>(deptPostIdSet, postIdSet); } + @Override + public List getCurrentActivityIds(String processInstanceId) { + List runExecutionList = + runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list(); + return runExecutionList.stream() + .map(Execution::getActivityId) + .filter(StrUtil::isNotBlank).collect(Collectors.toList()); + } + @Override public Map getAllUserTaskMap(String processDefinitionId) { BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId); diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowAutoVariableLogServiceImpl.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowAutoVariableLogServiceImpl.java new file mode 100644 index 00000000..c65be38b --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowAutoVariableLogServiceImpl.java @@ -0,0 +1,53 @@ +package com.orangeforms.common.flow.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.orangeforms.common.core.annotation.MyDataSourceResolver; +import com.orangeforms.common.core.base.dao.BaseDaoMapper; +import com.orangeforms.common.core.base.service.BaseService; +import com.orangeforms.common.core.constant.ApplicationConstant; +import com.orangeforms.common.core.util.DefaultDataSourceResolver; +import com.orangeforms.common.core.util.MyCommonUtil; +import com.orangeforms.common.flow.dao.FlowAutoVariableLogMapper; +import com.orangeforms.common.flow.model.FlowAutoVariableLog; +import com.orangeforms.common.flow.service.FlowAutoVariableLogService; +import com.orangeforms.common.sequence.wrapper.IdGeneratorWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; + +@Slf4j +@MyDataSourceResolver( + resolver = DefaultDataSourceResolver.class, + intArg = ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE) +@Service("flowAutoVariableLogService") +public class FlowAutoVariableLogServiceImpl extends BaseService implements FlowAutoVariableLogService { + + @Autowired + private FlowAutoVariableLogMapper flowAutoVariableLogMapper; + @Autowired + private IdGeneratorWrapper idGenerator; + + @Override + protected BaseDaoMapper mapper() { + return flowAutoVariableLogMapper; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void saveNew(FlowAutoVariableLog o) { + o.setId(idGenerator.nextLongId()); + o.setTraceId(MyCommonUtil.getTraceId()); + o.setCreateTime(new Date()); + flowAutoVariableLogMapper.insert(o); + } + + @Override + public FlowAutoVariableLog getAutoVariableByProcessInstanceId(String processInstanceId) { + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.eq(FlowAutoVariableLog::getProcessInstanceId, processInstanceId); + return flowAutoVariableLogMapper.selectOne(qw); + } +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowCategoryServiceImpl.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowCategoryServiceImpl.java index de1fb85a..d0ec0b85 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowCategoryServiceImpl.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowCategoryServiceImpl.java @@ -23,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Date; import java.util.List; import java.util.Set; + @Slf4j @MyDataSourceResolver( resolver = DefaultDataSourceResolver.class, diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowDblinkServiceImpl.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowDblinkServiceImpl.java new file mode 100644 index 00000000..0133050b --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowDblinkServiceImpl.java @@ -0,0 +1,142 @@ +package com.orangeforms.common.flow.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.github.pagehelper.Page; +import com.orangeforms.common.core.annotation.MyDataSourceResolver; +import com.orangeforms.common.core.base.dao.BaseDaoMapper; +import com.orangeforms.common.core.base.service.BaseService; +import com.orangeforms.common.core.constant.ApplicationConstant; +import com.orangeforms.common.core.object.MyRelationParam; +import com.orangeforms.common.core.object.TokenData; +import com.orangeforms.common.core.util.DefaultDataSourceResolver; +import com.orangeforms.common.dbutil.object.SqlTable; +import com.orangeforms.common.dbutil.object.SqlTableColumn; +import com.orangeforms.common.flow.dao.FlowDblinkMapper; +import com.orangeforms.common.flow.model.FlowDblink; +import com.orangeforms.common.flow.service.FlowDblinkService; +import com.orangeforms.common.flow.util.FlowDataSourceUtil; +import com.orangeforms.common.sequence.wrapper.IdGeneratorWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; + +@Slf4j +@MyDataSourceResolver( + resolver = DefaultDataSourceResolver.class, + intArg = ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE) +@Service("flowDblinkService") +public class FlowDblinkServiceImpl extends BaseService implements FlowDblinkService { + + @Autowired + private FlowDblinkMapper flowDblinkMapper; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private FlowDataSourceUtil dataSourceUtil; + + /** + * 返回当前Service的主表Mapper对象。 + * + * @return 主表Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return flowDblinkMapper; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public FlowDblink saveNew(FlowDblink flowDblink) { + flowDblinkMapper.insert(this.buildDefaultValue(flowDblink)); + return flowDblink; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(FlowDblink flowDblink, FlowDblink originalFlowDblink) { + if (!StrUtil.equals(flowDblink.getConfiguration(), originalFlowDblink.getConfiguration())) { + dataSourceUtil.removeDataSource(flowDblink.getDblinkId()); + } + flowDblink.setAppCode(TokenData.takeFromRequest().getAppCode()); + flowDblink.setCreateUserId(originalFlowDblink.getCreateUserId()); + flowDblink.setUpdateUserId(TokenData.takeFromRequest().getUserId()); + flowDblink.setCreateTime(originalFlowDblink.getCreateTime()); + flowDblink.setUpdateTime(new Date()); + // 这里重点提示,在执行主表数据更新之前,如果有哪些字段不支持修改操作,请用原有数据对象字段替换当前数据字段。 + UpdateWrapper uw = this.createUpdateQueryForNullValue(flowDblink, flowDblink.getDblinkId()); + return flowDblinkMapper.update(flowDblink, uw) == 1; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long dblinkId) { + dataSourceUtil.removeDataSource(dblinkId); + return flowDblinkMapper.deleteById(dblinkId) == 1; + } + + @Override + public List getFlowDblinkList(FlowDblink filter, String orderBy) { + if (filter == null) { + filter = new FlowDblink(); + } + filter.setAppCode(TokenData.takeFromRequest().getAppCode()); + return flowDblinkMapper.getFlowDblinkList(filter, orderBy); + } + + @Override + public List getFlowDblinkListWithRelation(FlowDblink filter, String orderBy) { + List resultList = this.getFlowDblinkList(filter, orderBy); + // 在缺省生成的代码中,如果查询结果resultList不是Page对象,说明没有分页,那么就很可能是数据导出接口调用了当前方法。 + // 为了避免一次性的大量数据关联,规避因此而造成的系统运行性能冲击,这里手动进行了分批次读取,开发者可按需修改该值。 + int batchSize = resultList instanceof Page ? 0 : 1000; + this.buildRelationForDataList(resultList, MyRelationParam.normal(), batchSize); + return resultList; + } + + @Override + public List getDblinkTableList(FlowDblink dblink) { + List resultList = dataSourceUtil.getTableList(dblink.getDblinkId(), null); + resultList.forEach(t -> t.setDblinkId(dblink.getDblinkId())); + return resultList; + } + + @Override + public SqlTable getDblinkTable(FlowDblink dblink, String tableName) { + if (dblink.getDblinkId() == null || StrUtil.isBlank(tableName)) { + return null; + } + SqlTable sqlTable = dataSourceUtil.getTable(dblink.getDblinkId(), tableName); + sqlTable.setDblinkId(dblink.getDblinkId()); + sqlTable.setColumnList(getDblinkTableColumnList(dblink, tableName)); + return sqlTable; + } + + @Override + public List getDblinkTableColumnList(FlowDblink dblink, String tableName) { + List columnList = dataSourceUtil.getTableColumnList(dblink.getDblinkId(), tableName); + columnList.forEach(c -> this.makeupSqlTableColumn(c, dblink.getDblinkType())); + return columnList; + } + + private void makeupSqlTableColumn(SqlTableColumn sqlTableColumn, int dblinkType) { + sqlTableColumn.setDblinkType(dblinkType); + sqlTableColumn.setAutoIncrement("auto_increment".equals(sqlTableColumn.getExtra())); + } + + private FlowDblink buildDefaultValue(FlowDblink flowDblink) { + flowDblink.setDblinkId(idGenerator.nextLongId()); + TokenData tokenData = TokenData.takeFromRequest(); + flowDblink.setCreateUserId(tokenData.getUserId()); + flowDblink.setUpdateUserId(tokenData.getUserId()); + Date now = new Date(); + flowDblink.setCreateTime(now); + flowDblink.setUpdateTime(now); + flowDblink.setAppCode(tokenData.getAppCode()); + return flowDblink; + } +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowEntryServiceImpl.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowEntryServiceImpl.java index adb7dca0..4cf97bf3 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowEntryServiceImpl.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowEntryServiceImpl.java @@ -23,6 +23,7 @@ import com.orangeforms.common.flow.dao.FlowEntryPublishVariableMapper; import com.orangeforms.common.flow.listener.*; import com.orangeforms.common.flow.model.*; import com.orangeforms.common.flow.model.constant.FlowEntryStatus; +import com.orangeforms.common.flow.model.constant.FlowEntryType; import com.orangeforms.common.flow.model.constant.FlowVariableType; import com.orangeforms.common.flow.object.FlowElementExtProperty; import com.orangeforms.common.flow.object.FlowEntryExtensionData; @@ -34,9 +35,7 @@ import com.orangeforms.common.flow.util.FlowCustomExtFactory; import com.orangeforms.common.flow.util.FlowRedisKeyUtil; import com.orangeforms.common.redis.util.CommonRedisUtil; import com.orangeforms.common.sequence.wrapper.IdGeneratorWrapper; -import lombok.Cleanup; import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.converter.BpmnXMLConverter; import org.flowable.bpmn.model.*; import org.flowable.engine.RepositoryService; import org.flowable.engine.repository.Deployment; @@ -45,12 +44,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; @@ -116,19 +109,16 @@ public class FlowEntryServiceImpl extends BaseService implement @Transactional(rollbackFor = Exception.class) @Override - public void publish(FlowEntry flowEntry, String initTaskInfo) throws XMLStreamException { - commonRedisUtil.evictFormCache( - FlowRedisKeyUtil.makeFlowEntryKey(flowEntry.getProcessDefinitionKey())); + public void publish(FlowEntry flowEntry, String initTaskInfo) { + commonRedisUtil.evictFormCache(FlowRedisKeyUtil.makeFlowEntryKey(flowEntry.getProcessDefinitionKey())); FlowCategory flowCategory = flowCategoryService.getById(flowEntry.getCategoryId()); - InputStream xmlStream = new ByteArrayInputStream( - flowEntry.getBpmnXml().getBytes(StandardCharsets.UTF_8)); - @Cleanup XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(xmlStream); - BpmnXMLConverter converter = new BpmnXMLConverter(); - BpmnModel bpmnModel = converter.convertToBpmnModel(reader); + BpmnModel bpmnModel = flowApiService.convertToBpmnModel(flowEntry.getBpmnXml()); bpmnModel.getMainProcess().setName(flowEntry.getProcessDefinitionName()); bpmnModel.getMainProcess().setId(flowEntry.getProcessDefinitionKey()); + this.processAutomaticTask(flowEntry, bpmnModel); flowApiService.addProcessInstanceEndListener(bpmnModel, FlowFinishedListener.class); List flowTaskExtList = flowTaskExtService.buildTaskExtList(bpmnModel); + flowTaskExtList.forEach(t -> flowTaskExtService.verifyAutoTaskConfig(t)); if (StrUtil.isNotBlank(flowEntry.getExtensionData())) { FlowEntryExtensionData flowEntryExtensionData = JSON.parseObject(flowEntry.getExtensionData(), FlowEntryExtensionData.class); @@ -343,6 +333,18 @@ public class FlowEntryServiceImpl extends BaseService implement return CallResult.ok(); } + private void processAutomaticTask(FlowEntry flowEntry, BpmnModel bpmnModel) { + if (flowEntry.getFlowType().equals(FlowEntryType.NORMAL_TYPE)) { + return; + } + List receiveTasks = bpmnModel.getMainProcess().getFlowElements() + .stream().filter(ReceiveTask.class::isInstance).map(ReceiveTask.class::cast).toList(); + receiveTasks.forEach(r -> { + flowApiService.addExecutionListener(r, ReceiveTaskStartListener.class, "start", null); + flowApiService.addExecutionListener(r, ReceiveTaskEndListener.class, "end", null); + }); + } + private void insertBuiltinEntryVariables(Long entryId) { Date now = new Date(); FlowEntryVariable operationTypeVariable = new FlowEntryVariable(); @@ -447,6 +449,9 @@ public class FlowEntryServiceImpl extends BaseService implement .filter(UserTask.class::isInstance).collect(Collectors.toMap(FlowElement::getId, c -> c)); BaseFlowIdentityExtHelper flowIdentityExtHelper = flowCustomExtFactory.getFlowIdentityExtHelper(); for (FlowTaskExt t : flowTaskExtList) { + if (!elementMap.containsKey(t.getTaskId())) { + continue; + } UserTask userTask = (UserTask) elementMap.get(t.getTaskId()); flowApiService.addTaskCreateListener(userTask, FlowUserTaskListener.class); Map> attributes = userTask.getAttributes(); diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowTaskExtServiceImpl.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowTaskExtServiceImpl.java index 54bd7ac1..b8720d22 100644 --- a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowTaskExtServiceImpl.java +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowTaskExtServiceImpl.java @@ -2,6 +2,7 @@ package com.orangeforms.common.flow.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.text.StrFormatter; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; @@ -15,7 +16,9 @@ import com.orangeforms.common.core.exception.MyRuntimeException; import com.orangeforms.common.core.object.Tuple2; import com.orangeforms.common.core.util.DefaultDataSourceResolver; import com.orangeforms.common.flow.constant.FlowApprovalType; +import com.orangeforms.common.flow.constant.FlowAutoActionType; import com.orangeforms.common.flow.constant.FlowConstant; +import com.orangeforms.common.flow.object.AutoTaskConfig; import com.orangeforms.common.flow.object.FlowElementExtProperty; import com.orangeforms.common.flow.object.FlowTaskMultiSignAssign; import com.orangeforms.common.flow.object.FlowUserTaskExtData; @@ -221,16 +224,29 @@ public class FlowTaskExtServiceImpl extends BaseService imp return resultUserMapList; } - private void buildUserMapList( - List userInfoList, Set loginNameSet, List userMapList) { - if (CollUtil.isEmpty(userInfoList)) { + @Override + public void verifyAutoTaskConfig(FlowTaskExt taskExt) { + if (StrUtil.isBlank(taskExt.getAutoConfigJson())) { return; } - for (FlowUserInfoVo userInfo : userInfoList) { - if (!loginNameSet.contains(userInfo.getLoginName())) { - loginNameSet.add(userInfo.getLoginName()); - userMapList.add(userInfo); + AutoTaskConfig taskConfig = JSON.parseObject(taskExt.getAutoConfigJson(), AutoTaskConfig.class); + this.verifyDataOperationTask(taskConfig); + String errorMessage; + if (taskConfig.getActionType().equals(FlowAutoActionType.SELECT_ONE)) { + this.verifySrcTableNameEmpty(taskConfig); + } else if (taskConfig.getActionType().equals(FlowAutoActionType.AGGREGATION_CALC)) { + this.verifySrcTableNameEmpty(taskConfig); + if (CollUtil.isEmpty(taskConfig.getAggregationDataList())) { + errorMessage = StrFormatter.format("任务 [{}] 的聚合计算配置不能为空!", taskConfig.getTaskName()); + throw new MyRuntimeException(errorMessage); } + } else if (taskConfig.getActionType().equals(FlowAutoActionType.NUMBER_CALC)) { + if (StrUtil.isBlank(taskConfig.getCalculateFormula())) { + errorMessage = StrFormatter.format("任务 [{}] 的数值计算公式不能为空!", taskConfig.getTaskName()); + throw new MyRuntimeException(errorMessage); + } + } else if (taskConfig.getActionType().equals(FlowAutoActionType.HTTP)) { + this.verifyHttpConfig(taskConfig); } } @@ -372,10 +388,93 @@ public class FlowTaskExtServiceImpl extends BaseService imp return propertiesData; } + private void verifyDataOperationTask(AutoTaskConfig taskConfig) { + String errorMessage; + if (taskConfig.getActionType().equals(FlowAutoActionType.ADD_NEW)) { + this.verifyDestTableNameEmpty(taskConfig); + if (CollUtil.isEmpty(taskConfig.getInsertDataList())) { + errorMessage = StrFormatter.format("任务 [{}] 的数据新增配置不能为空!", taskConfig.getTaskName()); + throw new MyRuntimeException(errorMessage); + } + } else if (taskConfig.getActionType().equals(FlowAutoActionType.UPDATE)) { + this.verifyDestTableNameEmpty(taskConfig); + if (CollUtil.isEmpty(taskConfig.getUpdateDataList())) { + errorMessage = StrFormatter.format("任务 [{}] 的数据更新配置不能为空!", taskConfig.getTaskName()); + throw new MyRuntimeException(errorMessage); + } + } else if (taskConfig.getActionType().equals(FlowAutoActionType.DELETE)) { + this.verifyDestTableNameEmpty(taskConfig); + + } + } + + private void verifyDestTableNameEmpty(AutoTaskConfig taskConfig) { + if (StrUtil.isBlank(taskConfig.getDestTableName())) { + String errorMessage = StrFormatter.format("任务 [{}] 的目标表不能为空!", taskConfig.getTaskName()); + throw new MyRuntimeException(errorMessage); + } + } + + private void verifySrcTableNameEmpty(AutoTaskConfig taskConfig) { + if (StrUtil.isBlank(taskConfig.getSrcTableName())) { + String errorMessage = StrFormatter.format("任务 [{}] 的源表不能为空!", taskConfig.getTaskName()); + throw new MyRuntimeException(errorMessage); + } + } + + private void verifyHttpConfig(AutoTaskConfig taskConfig) { + if (StrUtil.isBlank(taskConfig.getHttpRequestInfo().getUrl())) { + String errorMessage = StrFormatter.format("任务 [{}] 的URL不能为空!", taskConfig.getTaskName()); + throw new MyRuntimeException(errorMessage); + } + } + + private void buildUserMapList( + List userInfoList, Set loginNameSet, List userMapList) { + if (CollUtil.isEmpty(userInfoList)) { + return; + } + for (FlowUserInfoVo userInfo : userInfoList) { + if (!loginNameSet.contains(userInfo.getLoginName())) { + loginNameSet.add(userInfo.getLoginName()); + userMapList.add(userInfo); + } + } + } + + private FlowTaskExt buildTaskExtByAutoTask(Task task) { + FlowTaskExt flowTaskExt = new FlowTaskExt(); + flowTaskExt.setTaskId(task.getId()); + flowTaskExt.setGroupType(FlowConstant.GROUP_TYPE_AUTO_EXEC); + Map> extensionMap = task.getExtensionElements(); + List autoConfigElements = extensionMap.get("taskInfo"); + if (CollUtil.isEmpty(autoConfigElements)) { + return flowTaskExt; + } + ExtensionElement autoConfigElement = autoConfigElements.get(0); + Map> attributesMap = autoConfigElement.getAttributes(); + if (attributesMap != null) { + List attributes = attributesMap.get("data"); + if (CollUtil.isNotEmpty(attributes)) { + ExtensionAttribute attribute = attributes.get(0); + if (StrUtil.isNotBlank(attribute.getValue())) { + AutoTaskConfig taskConfig = JSONObject.parseObject(attribute.getValue(), AutoTaskConfig.class); + taskConfig.setTaskKey(task.getId()); + taskConfig.setTaskName(task.getName()); + flowTaskExt.setAutoConfigJson(JSON.toJSONString(taskConfig)); + } + } + } + return flowTaskExt; + } + private void doBuildTaskExtList(FlowElement element, List flowTaskExtList) { if (element instanceof UserTask) { FlowTaskExt flowTaskExt = this.buildTaskExtByUserTask((UserTask) element); flowTaskExtList.add(flowTaskExt); + } else if (element instanceof ServiceTask || element instanceof ReceiveTask) { + FlowTaskExt flowTaskExt = this.buildTaskExtByAutoTask((Task) element); + flowTaskExtList.add(flowTaskExt); } else if (element instanceof SubProcess) { Collection flowElements = ((SubProcess) element).getFlowElements(); for (FlowElement element1 : flowElements) { diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowTransProducerServiceImpl.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowTransProducerServiceImpl.java new file mode 100644 index 00000000..d99af60b --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/service/impl/FlowTransProducerServiceImpl.java @@ -0,0 +1,68 @@ +package com.orangeforms.common.flow.service.impl; + +import com.orangeforms.common.core.annotation.MyDataSourceResolver; +import com.orangeforms.common.core.base.dao.BaseDaoMapper; +import com.orangeforms.common.core.base.service.BaseService; +import com.orangeforms.common.core.constant.ApplicationConstant; +import com.orangeforms.common.core.object.TokenData; +import com.orangeforms.common.core.util.DefaultDataSourceResolver; +import com.orangeforms.common.core.util.MyCommonUtil; +import com.orangeforms.common.flow.dao.FlowTransProducerMapper; +import com.orangeforms.common.flow.model.FlowTransProducer; +import com.orangeforms.common.flow.service.FlowTransProducerService; +import com.orangeforms.common.sequence.wrapper.IdGeneratorWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.Serializable; +import java.util.Date; + +@Slf4j +@MyDataSourceResolver( + resolver = DefaultDataSourceResolver.class, + intArg = ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE) +@Service("flowTransProducerService") +public class FlowTransProducerServiceImpl extends BaseService implements FlowTransProducerService { + + @Autowired + private FlowTransProducerMapper flowTransProducerMapper; + @Autowired + private IdGeneratorWrapper idGenerator; + + @Override + protected BaseDaoMapper mapper() { + return flowTransProducerMapper; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public FlowTransProducer saveNew(FlowTransProducer data) { + if (data.getTransId() == null) { + data.setTransId(idGenerator.nextLongId()); + } + TokenData tokenData = TokenData.takeFromRequest(); + if (tokenData != null) { + data.setAppCode(tokenData.getAppCode()); + data.setCreateLoginName(tokenData.getLoginName()); + data.setCreateUsername(tokenData.getShowName()); + } + data.setCreateTime(new Date()); + data.setTraceId(MyCommonUtil.getTraceId()); + flowTransProducerMapper.insert(data); + return data; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public boolean updateById(FlowTransProducer data) { + return mapper().updateById(data) != 0; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public boolean removeById(Serializable transId) { + return mapper().deleteById(transId) != 0; + } +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/util/AutoFlowHelper.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/util/AutoFlowHelper.java new file mode 100644 index 00000000..7269bc41 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/util/AutoFlowHelper.java @@ -0,0 +1,657 @@ +package com.orangeforms.common.flow.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpStatus; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.JSONPath; +import com.orangeforms.common.core.constant.ApplicationConstant; +import com.orangeforms.common.core.constant.FieldFilterType; +import com.orangeforms.common.core.constant.GlobalDeletedFlag; +import com.orangeforms.common.core.exception.MyRuntimeException; +import com.orangeforms.common.core.object.TokenData; +import com.orangeforms.common.core.object.Tuple2; +import com.orangeforms.common.dbutil.object.DatasetFilter; +import com.orangeforms.common.dbutil.object.SqlTable; +import com.orangeforms.common.dbutil.object.SqlTableColumn; +import com.orangeforms.common.dbutil.util.DataSourceUtil; +import com.orangeforms.common.flow.constant.FlowAutoActionType; +import com.orangeforms.common.flow.model.FlowAutoVariableLog; +import com.orangeforms.common.flow.model.FlowDblink; +import com.orangeforms.common.flow.model.FlowEntryVariable; +import com.orangeforms.common.flow.model.FlowTaskExt; +import com.orangeforms.common.flow.object.*; +import com.orangeforms.common.flow.service.FlowApiService; +import com.orangeforms.common.flow.service.FlowAutoVariableLogService; +import com.orangeforms.common.flow.service.FlowDblinkService; +import com.orangeforms.common.flow.service.FlowTaskExtService; +import com.orangeforms.common.sequence.wrapper.IdGeneratorWrapper; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.io.Serializable; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.*; +import java.util.stream.Collectors; + +import static com.orangeforms.common.flow.object.AutoTaskConfig.*; +import static com.orangeforms.common.flow.object.AutoTaskConfig.SRC_FILTER_SQL; + +@Slf4j +@Component +public class AutoFlowHelper { + + @Autowired + private FlowApiService flowApiService; + @Autowired + private FlowDblinkService flowDblinkService; + @Autowired + private FlowTaskExtService flowTaskExtService; + @Autowired + private FlowAutoVariableLogService flowAutoVariableLogService; + @Autowired + private FlowDataSourceUtil flowDataSourceUtil; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private RuntimeService runtimeService; + @Autowired + private RestTemplate restTemplate; + + private static final String REGEX_VAR = "\\$\\{(.+?)\\}"; + private static final ThreadLocal START_AUTO_INIT_VARIABLES = ThreadLocal.withInitial(() -> null); + private static final List SYSTEM_VARIABLES = CollUtil.newLinkedList(); + + static { + SYSTEM_VARIABLES.add(FlowEntryVariable.createSystemVariable("currentTime", "当前时间")); + SYSTEM_VARIABLES.add(FlowEntryVariable.createSystemVariable("initialUserId", "发起用户ID")); + SYSTEM_VARIABLES.add(FlowEntryVariable.createSystemVariable("initialLoginName", "发起用户登录名")); + SYSTEM_VARIABLES.add(FlowEntryVariable.createSystemVariable("initialShowName", "发起用户显示名")); + SYSTEM_VARIABLES.add(FlowEntryVariable.createSystemVariable("initialDeptId", "发起用户部门ID")); + SYSTEM_VARIABLES.add(FlowEntryVariable.createSystemVariable("tokenData", "Token令牌数据")); + SYSTEM_VARIABLES.add(FlowEntryVariable.createSystemVariable("logicNormal", "逻辑删除正常值")); + SYSTEM_VARIABLES.add(FlowEntryVariable.createSystemVariable("logicDelete", "逻辑删除删除值")); + } + + /** + * 设置初始化变量,仅在启动自动化流程的时候存入。临时存入变量参数到线程本地化存储,以后任务监听器的读取。 + * + * @param variables 自动化流程启动时的初始变量对象。 + */ + public static void setStartAutoInitVariables(JSONObject variables) { + START_AUTO_INIT_VARIABLES.set(variables); + } + + /** + * 获取自动化流程启动时传入的初始变量数据。 + * + * @return 自动化流程启动时传入的初始变量数据。 + */ + public static JSONObject getStartAutoInitVariables() { + return START_AUTO_INIT_VARIABLES.get(); + } + + /** + * 清空该存储数据,主动释放线程本地化存储资源。 + */ + public static void clearStartAutoInitVariables() { + START_AUTO_INIT_VARIABLES.remove(); + } + + public static List systemFlowEntryVariables() { + return SYSTEM_VARIABLES; + } + + /** + * 获取实时的系统变量。 + * + * @return 系统变量键值对对象。 + */ + public JSONObject getRealtimeSystemVariables() { + JSONObject systemVariables = new JSONObject(); + systemVariables.put("currentTime", new Date()); + return systemVariables; + } + + /** + * 获取非实时的系统变量。 + * + * @return 系统变量键值对对象。 + */ + public JSONObject getNonRealtimeSystemVariables() { + JSONObject systemVariables = this.getRealtimeSystemVariables(); + TokenData tokenData = TokenData.takeFromRequest(); + systemVariables.put("initialUserId", tokenData != null ? tokenData.getUserId() : null); + systemVariables.put("initialLoginName", tokenData != null ? tokenData.getLoginName() : null); + systemVariables.put("initialShowName", tokenData != null ? tokenData.getShowName() : null); + systemVariables.put("initialDeptId", tokenData != null ? tokenData.getDeptId() : null); + systemVariables.put("tokenData", tokenData != null ? tokenData.getToken() : null); + systemVariables.put("logicNormal", GlobalDeletedFlag.NORMAL); + systemVariables.put("logicDelete", GlobalDeletedFlag.DELETED); + return systemVariables; + } + + /** + * 解析自动化任务配置对象。 + * + * @param processDefinitionId 流程定义Id。 + * @param taskKey 流程任务定义标识。 + * @return 自动化任务的配置对象。 + */ + public AutoTaskConfig parseAutoTaskConfig(String processDefinitionId, String taskKey) { + FlowTaskExt taskExt = flowTaskExtService.getByProcessDefinitionIdAndTaskId(processDefinitionId, taskKey); + return JSON.parseObject(taskExt.getAutoConfigJson(), AutoTaskConfig.class); + } + + /** + * 指定指定的任务。 + * + * @param transId 自动化任务执行时的生产者流水号Id。 + * @param taskConfig 自动化任务配置对象。 + * @param d 当前的执行委托对象。 + */ + public void executeTask(Long transId, AutoTaskConfig taskConfig, DelegateExecution d) { + JSONObject variableData = this.getAutomaticVariable(d.getProcessInstanceId()); + FlowDblink destDblink = new FlowDblink(); + destDblink.setDblinkId(taskConfig.getDestDblinkId()); + destDblink.setDblinkType(taskConfig.getDestDblinkType()); + SqlTable destTable = flowDblinkService.getDblinkTable(destDblink, taskConfig.getDestTableName()); + if (taskConfig.getActionType().equals(FlowAutoActionType.ADD_NEW)) { + List execDataList = this.makeInsertAutoExecData(taskConfig, destTable, variableData); + this.doExecuteSql(execDataList); + } else if (taskConfig.getActionType().equals(FlowAutoActionType.UPDATE)) { + AutoExecData execData = this.makeUpdateAutoExecData(taskConfig, destTable, variableData); + this.doExecuteSql(CollUtil.newArrayList(execData)); + } else if (taskConfig.getActionType().equals(FlowAutoActionType.DELETE)) { + AutoExecData execData = this.makeDeleteAutoExecData(taskConfig, destTable, variableData); + this.doExecuteSql(CollUtil.newArrayList(execData)); + } else if (taskConfig.getActionType().equals(FlowAutoActionType.SELECT_ONE)) { + this.doQueryOne(taskConfig, variableData, d); + } else if (taskConfig.getActionType().equals(FlowAutoActionType.HTTP)) { + this.doHttpRequest(transId, taskConfig, variableData, d); + } + } + + private void doExecuteSql(List autoExecDataList) { + if (CollUtil.isEmpty(autoExecDataList)) { + return; + } + Connection connection = null; + try { + connection = flowDataSourceUtil.getConnection(autoExecDataList.get(0).getDblinkId()); + connection.setAutoCommit(false); + for (AutoExecData autoExecData : autoExecDataList) { + flowDataSourceUtil.execute(connection, autoExecData.getSql(), autoExecData.getParams()); + } + connection.commit(); + } catch (Exception e) { + if (connection != null) { + try { + connection.rollback(); + } catch (SQLException ex) { + log.error(e.getMessage(), e); + } + } + log.error(e.getMessage(), e); + throw new MyRuntimeException(e); + } finally { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + log.error(e.getMessage(), e); + } + } + } + } + + private List makeInsertAutoExecData( + AutoTaskConfig taskConfig, SqlTable destTable, JSONObject variableData) { + List resultList = new LinkedList<>(); + SqlTableColumn primaryKeyColumn = destTable.getColumnList().stream() + .filter(c -> BooleanUtil.isTrue(c.getPrimaryKey())).findFirst().orElse(null); + Map destColumnMap = + destTable.getColumnList().stream().collect(Collectors.toMap(SqlTableColumn::getColumnName, c -> c)); + ValueInfo pkValueInfo = null; + if (primaryKeyColumn != null) { + pkValueInfo = taskConfig.getInsertDataList().stream() + .filter(valueInfo -> valueInfo.getDestColumnName().equals(primaryKeyColumn.getColumnName())) + .findFirst().orElse(null); + } + //查询源数据。 + List> srcResultList = this.getSrcTableDataList(taskConfig, variableData); + if (CollUtil.isEmpty(srcResultList)) { + srcResultList.add(new HashMap<>(1)); + } + for (Map srcResult : srcResultList) { + List params = new LinkedList<>(); + StringBuilder sqlBuilder = new StringBuilder(1024); + sqlBuilder.append("INSERT INTO ").append(taskConfig.getDestTableName()).append("("); + if (primaryKeyColumn != null) { + Object param = this.calculatePrimaryKeyParam(primaryKeyColumn, pkValueInfo, srcResult); + if (param != null) { + sqlBuilder.append(primaryKeyColumn.getColumnName()).append(","); + params.add(param); + } + } + for (ValueInfo valueInfo : taskConfig.getInsertDataList()) { + if (pkValueInfo == null || !pkValueInfo.equals(valueInfo)) { + sqlBuilder.append(valueInfo.getDestColumnName()).append(","); + SqlTableColumn column = destColumnMap.get(valueInfo.getDestColumnName()); + String destFieldType = flowDataSourceUtil.convertToJavaType(column, taskConfig.getDestDblinkType()); + Object value = this.calculateValue( + valueInfo.getType(), valueInfo.getSrcValue(), destFieldType, srcResult, variableData); + params.add(value); + } + } + sqlBuilder.setCharAt(sqlBuilder.length() - 1, ')'); + sqlBuilder.append(" VALUES("); + params.forEach(p -> sqlBuilder.append("?,")); + sqlBuilder.setCharAt(sqlBuilder.length() - 1, ')'); + AutoExecData execData = new AutoExecData(); + execData.setDblinkId(taskConfig.getDestDblinkId()); + execData.setSql(sqlBuilder.toString()); + execData.setParams(params); + resultList.add(execData); + } + return resultList; + } + + private AutoExecData makeUpdateAutoExecData(AutoTaskConfig taskConfig, SqlTable destTable, JSONObject variableData) { + Map destColumnMap = + destTable.getColumnList().stream().collect(Collectors.toMap(SqlTableColumn::getColumnName, c -> c)); + List params = new LinkedList<>(); + StringBuilder sqlBuilder = new StringBuilder(1024); + sqlBuilder.append("UPDATE ").append(taskConfig.getDestTableName()).append(" SET "); + for (ValueInfo valueInfo : taskConfig.getUpdateDataList()) { + sqlBuilder.append(valueInfo.getDestColumnName()).append(" = ?,"); + SqlTableColumn column = destColumnMap.get(valueInfo.getDestColumnName()); + String destFieldType = flowDataSourceUtil.convertToJavaType(column, taskConfig.getDestDblinkType()); + Object value = this.calculateValue( + valueInfo.getType(), valueInfo.getSrcValue(), destFieldType, null, variableData); + params.add(value); + } + sqlBuilder.setCharAt(sqlBuilder.length() - 1, ' '); + Tuple2> result = this.calculateWhereClause(taskConfig, destTable, variableData); + sqlBuilder.append(result.getFirst()); + CollUtil.addAll(params, result.getSecond()); + AutoExecData execData = new AutoExecData(); + execData.setDblinkId(taskConfig.getDestDblinkId()); + execData.setSql(sqlBuilder.toString()); + execData.setParams(params); + return execData; + } + + private AutoExecData makeDeleteAutoExecData(AutoTaskConfig taskConfig, SqlTable destTable, JSONObject variableData) { + if (StrUtil.isNotBlank(taskConfig.getLogicDeleteField())) { + List updateDataList = new LinkedList<>(); + ValueInfo logicDeleteValueInfo = new ValueInfo(); + logicDeleteValueInfo.setDestColumnName(taskConfig.getLogicDeleteField()); + logicDeleteValueInfo.setType(AutoTaskConfig.FIXED_VALUE); + logicDeleteValueInfo.setSrcValue(String.valueOf(GlobalDeletedFlag.DELETED)); + updateDataList.add(logicDeleteValueInfo); + taskConfig.setUpdateDataList(updateDataList); + return this.makeUpdateAutoExecData(taskConfig, destTable, variableData); + } + StringBuilder sqlBuilder = new StringBuilder(1024); + sqlBuilder.append("DELETE FROM ").append(taskConfig.getDestTableName()); + Tuple2> result = this.calculateWhereClause(taskConfig, destTable, variableData); + sqlBuilder.append(result.getFirst()); + AutoExecData execData = new AutoExecData(); + execData.setDblinkId(taskConfig.getDestDblinkId()); + execData.setSql(sqlBuilder.toString()); + execData.setParams(result.getSecond()); + return execData; + } + + private void doQueryOne(AutoTaskConfig taskConfig, JSONObject variableData, DelegateExecution d) { + List> srcResultList = this.getSrcTableDataList(taskConfig, variableData); + Map srcResult = null; + if (CollUtil.isNotEmpty(srcResultList)) { + srcResult = srcResultList.get(0); + } + this.refreshAutoVariableLog(d, taskConfig.getTaskKey(), srcResult); + } + + private void doHttpRequest(Long transId, AutoTaskConfig taskConfig, JSONObject variableData, DelegateExecution d) { + AutoHttpRequestInfo req = taskConfig.getHttpRequestInfo(); + AutoHttpResponseData resp = taskConfig.getHttpResponnseData(); + String body = this.buildRequestBody(req, variableData); + HttpHeaders headers = this.buildHttpHeaders(req, variableData); + headers.add(ApplicationConstant.HTTP_HEADER_TRANS_ID, transId.toString()); + HttpEntity httpEntity = new HttpEntity<>(body, headers); + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(req.getUrl()); + if (CollUtil.isNotEmpty(req.getUrlParamList())) { + for (AutoTaskConfig.ValueInfo valueInfo : req.getUrlParamList()) { + String paramValue = this.calculateValue(valueInfo.getType(), valueInfo.getSrcValue(), variableData); + uriBuilder.queryParam(valueInfo.getKey(), paramValue); + } + } + ResponseEntity responseEntity = restTemplate.exchange( + uriBuilder.encode().toUriString(), HttpMethod.valueOf(req.getHttpMethod()), httpEntity, JSONObject.class); + try { + this.handleHttpResponseFail(req, resp, responseEntity); + this.refreshAutoVariableLog(d, taskConfig.getTaskKey(), responseEntity.getBody()); + } catch (MyRuntimeException e) { + if (!resp.getFailHandleType().equals(AutoHttpResponseData.CONTINUE_ON_FAIL)) { + throw e; + } + log.error(e.getMessage(), e); + } + } + + private Object calculatePrimaryKeyParam( + SqlTableColumn primaryKeyColumn, ValueInfo pkValueInfo, Map srcResult) { + // 说明目标表的主键值来自于源表的字段值。 + if (pkValueInfo != null) { + return srcResult.get(pkValueInfo.getSrcValue()); + } + if (BooleanUtil.isFalse(primaryKeyColumn.getAutoIncrement())) { + return primaryKeyColumn.getStringPrecision() == null ? idGenerator.nextLongId() : idGenerator.nextStringId(); + } + return null; + } + + private List> getSrcTableDataList(AutoTaskConfig taskConfig, JSONObject variableData) { + if (ObjectUtil.isEmpty(taskConfig.getSrcDblinkId()) || StrUtil.isBlank(taskConfig.getSrcTableName())) { + return new LinkedList<>(); + } + this.appendLogicDeleteFilter(taskConfig); + FlowDblink srcDblink = new FlowDblink(); + srcDblink.setDblinkId(taskConfig.getSrcDblinkId()); + srcDblink.setDblinkType(taskConfig.getSrcDblinkType()); + SqlTable srcTable = flowDblinkService.getDblinkTable(srcDblink, taskConfig.getSrcTableName()); + StringBuilder sqlBuilder = new StringBuilder(256); + String selectFields = CollUtil.isEmpty(taskConfig.getSelectFieldList()) + ? "*" : CollUtil.join(taskConfig.getSelectFieldList(), ","); + sqlBuilder.append("SELECT ").append(selectFields).append(" FROM ").append(srcTable.getTableName()); + if (taskConfig.getSrcFilterType().equals(AutoTaskConfig.SRC_FILTER_SQL)) { + Tuple2> result = this.calcualteCustomSqlFilter(taskConfig.getSrcFilterSql(), variableData); + if (StrUtil.isNotBlank(result.getFirst())) { + sqlBuilder.append(result.getFirst()); + } + try { + return flowDataSourceUtil.query(taskConfig.getSrcDblinkId(), sqlBuilder.toString(), result.getSecond()); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new MyRuntimeException(e); + } + } + DatasetFilter dataFilter = null; + if (CollUtil.isNotEmpty(taskConfig.getSrcFilterList())) { + dataFilter = this.calculateDatasetFilter( + srcTable, taskConfig.getSrcDblinkType(), taskConfig.getSrcFilterList(), variableData); + } + try { + Tuple2> result = + flowDataSourceUtil.buildWhereClauseByFilters(taskConfig.getSrcDblinkId(), dataFilter); + sqlBuilder.append(result.getFirst()); + return flowDataSourceUtil.query(taskConfig.getSrcDblinkId(), sqlBuilder.toString(), result.getSecond()); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new MyRuntimeException(e); + } + } + + private void refreshAutoVariableLog(DelegateExecution d, String outputVariableName, Map newResult) { + if (StrUtil.isBlank(outputVariableName)) { + return; + } + runtimeService.setVariable(d.getId(), outputVariableName, newResult); + FlowAutoVariableLog autoVariableLog = + flowAutoVariableLogService.getAutoVariableByProcessInstanceId(d.getProcessInstanceId()); + JSONObject latestVariableData = JSON.parseObject(autoVariableLog.getVariableData()); + latestVariableData.put(outputVariableName, newResult); + autoVariableLog.setVariableData(JSON.toJSONString(latestVariableData)); + flowAutoVariableLogService.updateById(autoVariableLog); + } + + private Tuple2> calculateWhereClause(AutoTaskConfig taskConfig, SqlTable destTable, JSONObject variableData) { + if (taskConfig.getDestFilterType().equals(AutoTaskConfig.SRC_FILTER_SQL)) { + return this.calcualteCustomSqlFilter(taskConfig.getDestFilterSql(), variableData); + } + if (CollUtil.isNotEmpty(taskConfig.getDestFilterList())) { + DatasetFilter dataFilter = this.calculateDatasetFilter( + destTable, taskConfig.getDestDblinkType(), taskConfig.getDestFilterList(), variableData); + return flowDataSourceUtil.buildWhereClauseByFilters(taskConfig.getDestDblinkId(), dataFilter); + } + return new Tuple2<>(StrUtil.EMPTY, new LinkedList<>()); + } + + private Tuple2> calcualteCustomSqlFilter(String filterSql, JSONObject variableData) { + Tuple2> result = this.findAndReplaceAllVariables(filterSql); + String whereClause = result.getFirst(); + if (StrUtil.isNotBlank(whereClause)) { + whereClause = DataSourceUtil.SQL_WHERE + whereClause; + } + List params = this.extractVariableParamsByVariable(result.getSecond(), variableData); + return new Tuple2<>(whereClause, params); + } + + private DatasetFilter calculateDatasetFilter( + SqlTable table, Integer dblinkType, List filterInfoList, JSONObject variableData) { + Map columnMap = table.getColumnList() + .stream().collect(Collectors.toMap(SqlTableColumn::getColumnName, c -> c)); + DatasetFilter dataFilter = new DatasetFilter(); + filterInfoList.forEach(filterInfo -> { + DatasetFilter.FilterInfo filter = new DatasetFilter.FilterInfo(); + filter.setFilterType(filterInfo.getFilterType()); + filter.setParamName(filterInfo.getFilterColumnName()); + SqlTableColumn column = columnMap.get(filterInfo.getFilterColumnName()); + String fieldType = flowDataSourceUtil.convertToJavaType(column, dblinkType); + if (filterInfo.getFilterType().equals(FieldFilterType.IN)) { + filter.setParamValueList(flowDataSourceUtil.convertToColumnValues(fieldType, filterInfo.getFilterValue())); + } else { + Object convertedValue = calculateValue( + filterInfo.getValueType(), filterInfo.getFilterValue(), fieldType, null, variableData); + filter.setParamValue(convertedValue); + } + dataFilter.add(filter); + }); + return dataFilter; + } + + private void appendLogicDeleteFilter(AutoTaskConfig taskConfig) { + if (StrUtil.isBlank(taskConfig.getLogicDeleteField())) { + return; + } + if (taskConfig.getSrcFilterType().equals(SRC_FILTER_SQL)) { + StringBuilder sb = new StringBuilder(512); + if (StrUtil.isNotBlank(taskConfig.getSrcFilterSql())) { + sb.append("(").append(taskConfig.getSrcFilterSql()) + .append(") AND ") + .append(taskConfig.getLogicDeleteField()) + .append("=") + .append(GlobalDeletedFlag.NORMAL); + } else { + sb.append(taskConfig.getLogicDeleteField()).append("=").append(GlobalDeletedFlag.NORMAL); + } + taskConfig.setSrcFilterSql(sb.toString()); + } else { + List filterInfoList = taskConfig.getSrcFilterList(); + if (filterInfoList == null) { + filterInfoList = new LinkedList<>(); + } + FilterInfo logicDeleteFilter = new FilterInfo(); + logicDeleteFilter.setFilterColumnName(taskConfig.getLogicDeleteField()); + logicDeleteFilter.setFilterType(FieldFilterType.EQUAL); + logicDeleteFilter.setValueType(AutoTaskConfig.FIXED_VALUE); + logicDeleteFilter.setFilterValue(String.valueOf(GlobalDeletedFlag.NORMAL)); + filterInfoList.add(logicDeleteFilter); + taskConfig.setSrcFilterList(filterInfoList); + } + } + + private String buildRequestBody(AutoHttpRequestInfo req, JSONObject variableData) { + String body = null; + if (StrUtil.equals(req.getBodyType(), AutoHttpRequestInfo.BODY_TYPE_RAW)) { + body = this.replaceAllVariables(req.getBodyData(), variableData); + } else { + StringBuilder sb = new StringBuilder(256); + if (CollUtil.isNotEmpty(req.getFormDataList())) { + for (ValueInfo valueInfo : req.getFormDataList()) { + String value = this.calculateValue(valueInfo.getType(), valueInfo.getSrcValue(), variableData); + sb.append(valueInfo.getKey()).append("=").append(value).append("&"); + } + body = sb.substring(0, sb.length() - 1); + } + } + return body; + } + + private HttpHeaders buildHttpHeaders(AutoHttpRequestInfo req, JSONObject variableData) { + HttpHeaders headers = new HttpHeaders(); + if (CollUtil.isNotEmpty(req.getHeaderList())) { + for (AutoTaskConfig.ValueInfo valueInfo : req.getHeaderList()) { + String value = this.calculateValue(valueInfo.getType(), valueInfo.getSrcValue(), variableData); + headers.add(valueInfo.getKey(), value); + } + } + if (StrUtil.equals(req.getBodyType(), AutoHttpRequestInfo.BODY_TYPE_RAW)) { + headers.setContentType(MediaType.APPLICATION_JSON); + } else { + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + } + return headers; + } + + private void handleHttpResponseFail( + AutoHttpRequestInfo req, AutoHttpResponseData resp, ResponseEntity responseEntity) { + Set successStatusCodes = CollUtil.newHashSet(HttpStatus.HTTP_OK); + if (StrUtil.isNotBlank(resp.getSuccessStatusCode())) { + successStatusCodes = StrUtil.split(resp.getSuccessStatusCode(), StrUtil.COMMA) + .stream().map(Integer::valueOf).collect(Collectors.toSet()); + } + // 先判断HttpStatus是否正确。 + if (!CollUtil.contains(successStatusCodes, responseEntity.getStatusCode().value())) { + String cancelReason = StrFormatter.format( + "Failed to Rquest Url [{}] with StatusCode [{}]", req.getUrl(), responseEntity.getStatusCode().value()); + throw new MyRuntimeException(cancelReason); + } + // 如果没有配置应答体中标记是否成功的字段,则视为成功。 + if (StrUtil.isBlank(resp.getSuccessBodyField())) { + return; + } + JSONObject responseData = responseEntity.getBody(); + String successFlagField = JSONPath.compile(resp.getSuccessBodyField()).eval(responseData, String.class); + // 如果应答体中标记是否成功的字段你不存在,或者是应答体中标记为成功的字段值为true,则视为成功。 + if (StrUtil.isBlank(successFlagField) + || StrUtil.equals(successFlagField, "[]") + || BooleanUtil.toBooleanObject(successFlagField).equals(Boolean.TRUE)) { + return; + } + // 开始处理失败场景。 + String cancelReason; + if (StrUtil.isNotBlank(resp.getErrorMessageBodyField())) { + String errorMsgPath = "$." + resp.getErrorMessageBodyField(); + String errorMsg = JSONPath.compile(errorMsgPath).eval(responseData, String.class); + cancelReason = StrFormatter.format( + "Failed to Rquest Url [{}] with errorMsg [{}]", req.getUrl(), errorMsg); + } else { + cancelReason = StrFormatter.format("Failed to Rquest Url [{}]", req.getUrl()); + } + throw new MyRuntimeException(cancelReason); + } + + private Tuple2> findAndReplaceAllVariables(String s) { + List variables = ReUtil.findAll(REGEX_VAR, s, 0); + if (CollUtil.isNotEmpty(variables)) { + s = s.replaceAll(REGEX_VAR, "?"); + variables = variables.stream().map(v -> v.substring(2, v.length() - 1)).collect(Collectors.toList()); + } + return new Tuple2<>(s, variables); + } + + private String replaceAllVariables(String s, JSONObject variableData) { + if (StrUtil.isNotBlank(s)) { + List variables = ReUtil.findAll(REGEX_VAR, s, 0); + if (CollUtil.isNotEmpty(variables)) { + for (String v : variables) { + s = StrUtil.replace(s, v, variableData.getString(v.substring(2, v.length() - 1))); + } + } + } + return s; + } + + private JSONObject getAutomaticVariable(String processInstanceId) { + JSONObject variableData = getStartAutoInitVariables(); + if (variableData == null) { + FlowAutoVariableLog v = flowAutoVariableLogService.getAutoVariableByProcessInstanceId(processInstanceId); + if (v != null) { + variableData = JSON.parseObject(v.getVariableData()); + } + } + JSONObject systemVariables = this.getRealtimeSystemVariables(); + if (variableData == null) { + return systemVariables; + } + variableData.putAll(systemVariables); + return variableData; + } + + private List extractVariableParamsByVariable(List variableNames, JSONObject variableData) { + List resultList = new LinkedList<>(); + if (CollUtil.isEmpty(variableNames) || MapUtil.isEmpty(variableData)) { + return resultList; + } + for (String name : variableNames) { + Object value = this.verifyAndGetVariableExist(name, variableData); + if (value != null) { + resultList.add(value); + } + } + return resultList; + } + + private Object calculateValue( + Integer valueType, String value, String fieldType, Map srcResult, JSONObject variableData) { + if (valueType.equals(AutoTaskConfig.FIXED_VALUE)) { + return flowDataSourceUtil.convertToColumnValue(fieldType, value); + } else if (valueType.equals(AutoTaskConfig.COLUMN_VALUE)) { + return srcResult.get(value); + } + Object variableValue = this.verifyAndGetVariableExist(value, variableData); + return flowDataSourceUtil.convertToColumnValue(fieldType, (Serializable) variableValue); + } + + private String calculateValue(Integer valueType, String value, JSONObject variableData) { + if (valueType.equals(AutoTaskConfig.FIXED_VALUE)) { + return value; + } + Object variableValue = this.verifyAndGetVariableExist(value, variableData); + return variableValue == null ? null : variableValue.toString(); + } + + private Object verifyAndGetVariableExist(String name, JSONObject variableData) { + String variableName = name; + if (StrUtil.contains(name, StrUtil.DOT)) { + variableName = StrUtil.subBefore(name, StrUtil.DOT, false); + } + if (!variableData.containsKey(variableName)) { + throw new MyRuntimeException(StrFormatter.format("变量值 [{}] 不存在!", name)); + } + if (!StrUtil.contains(name, StrUtil.DOT)) { + return variableData.get(variableName); + } + JSONObject variableObject = variableData.getJSONObject(variableName); + if (variableObject == null) { + return null; + } + String variablePath = StrUtil.subAfter(name, StrUtil.DOT, false); + return JSONPath.compile(variablePath).eval(variableObject); + } +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/util/FlowDataSourceUtil.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/util/FlowDataSourceUtil.java new file mode 100644 index 00000000..a47513d1 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/util/FlowDataSourceUtil.java @@ -0,0 +1,47 @@ +package com.orangeforms.common.flow.util; + +import com.orangeforms.common.core.exception.MyRuntimeException; +import com.orangeforms.common.dbutil.provider.DataSourceProvider; +import com.orangeforms.common.dbutil.util.DataSourceUtil; +import com.orangeforms.common.flow.model.FlowDblink; +import com.orangeforms.common.flow.service.FlowDblinkService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 工作流模块动态加载的数据源工具类。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Slf4j +@Component +public class FlowDataSourceUtil extends DataSourceUtil { + + @Autowired + private FlowDblinkService dblinkService; + + @Override + protected int getDblinkTypeByDblinkId(Long dblinkId) { + DataSourceProvider provider = this.dblinkProviderMap.get(dblinkId); + if (provider != null) { + return provider.getDblinkType(); + } + FlowDblink dblink = dblinkService.getById(dblinkId); + if (dblink == null) { + throw new MyRuntimeException("Flow DblinkId [" + dblinkId + "] doesn't exist!"); + } + this.dblinkProviderMap.put(dblinkId, this.getProvider(dblink.getDblinkType())); + return dblink.getDblinkType(); + } + + @Override + protected String getDblinkConfigurationByDblinkId(Long dblinkId) { + FlowDblink dblink = dblinkService.getById(dblinkId); + if (dblink == null) { + throw new MyRuntimeException("Flow DblinkId [" + dblinkId + "] doesn't exist!"); + } + return dblink.getConfiguration(); + } +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/util/ListenerEventPublishHelper.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/util/ListenerEventPublishHelper.java new file mode 100644 index 00000000..c949aa89 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/util/ListenerEventPublishHelper.java @@ -0,0 +1,59 @@ +package com.orangeforms.common.flow.util; + +import com.alibaba.fastjson.JSON; +import com.orangeforms.common.core.exception.MyRuntimeException; +import com.orangeforms.common.flow.constant.FlowConstant; +import com.orangeforms.common.flow.model.FlowTransProducer; +import com.orangeforms.common.flow.service.FlowTransProducerService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.RuntimeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executors; + +/** + * 流程监听器发送spring事件的帮助类。 + * 注意:流程的监听器不是bean对象,不能发送和捕捉事件,因此需要借助该类完成。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Slf4j +@Component +public class ListenerEventPublishHelper { + + @Autowired + private ApplicationEventPublisher eventPublisher; + @Autowired + private RuntimeService runtimeService; + @Autowired + private FlowTransProducerService flowTransProducerService; + + public void publishEvent(T data) { + eventPublisher.publishEvent(data); + } + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void doHandle(FlowTransProducer producerData) { + Map transVariableMap = new HashMap<>(1); + transVariableMap.put(FlowConstant.AUTO_FLOW_TRANS_PRODUCER_VAR, producerData); + Executors.newSingleThreadExecutor().submit(() -> this.triggerReceiveTask(producerData, transVariableMap)); + } + + private void triggerReceiveTask(FlowTransProducer producerData, Map transVariableMap) { + try { + runtimeService.trigger(producerData.getExecutionId(), null, transVariableMap); + } catch (Exception e) { + log.error("Failed to commit automatic business data [** " + JSON.toJSONString(producerData) + " **]", e); + producerData.setErrorReason(e.getMessage()); + flowTransProducerService.updateById(producerData); + throw new MyRuntimeException(e.getMessage()); + } + } +} diff --git a/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/vo/FlowDblinkVo.java b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/vo/FlowDblinkVo.java new file mode 100644 index 00000000..f6438ab0 --- /dev/null +++ b/OrangeFormsOpen-MybatisPlus/common/common-flow/src/main/java/com/orangeforms/common/flow/vo/FlowDblinkVo.java @@ -0,0 +1,84 @@ +package com.orangeforms.common.flow.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; +import java.util.Map; + +/** + * 工作流数据表所在数据库链接VO对象。 + * + * @author Jerry + * @date 2024-07-02 + */ +@Schema(description = "工作流数据表所在数据库链接VO对象") +@Data +public class FlowDblinkVo { + + /** + * 主键Id。 + */ + @Schema(description = "主键Id") + private Long dblinkId; + + /** + * 应用编码。为空时,表示非第三方应用接入。 + */ + @Schema(description = "应用编码。为空时,表示非第三方应用接入") + private String appCode; + + /** + * 链接中文名称。 + */ + @Schema(description = "链接中文名称") + private String dblinkName; + + /** + * 链接描述。 + */ + @Schema(description = "链接描述") + private String dblinkDescription; + + /** + * 配置信息。 + */ + @Schema(description = "配置信息") + private String configuration; + + /** + * 数据库链接类型。 + */ + @Schema(description = "数据库链接类型") + private Integer dblinkType; + + /** + * 更新者。 + */ + @Schema(description = "更新者") + private Long updateUserId; + + /** + * 更新时间。 + */ + @Schema(description = "更新时间") + private Date updateTime; + + /** + * 创建者。 + */ + @Schema(description = "创建者") + private Long createUserId; + + /** + * 创建时间。 + */ + @Schema(description = "创建时间") + private Date createTime; + + /** + * 数据库链接类型常量字典关联数据。 + */ + @Schema(description = "数据库链接类型常量字典关联数据") + private Map dblinkTypeDictMap; +} diff --git a/OrangeFormsOpen-MybatisPlus/zz-resource/.DS_Store b/OrangeFormsOpen-MybatisPlus/zz-resource/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..49d83f84ba68bfc6a7265307097e4bf5a3e85d75 GIT binary patch literal 6148 zcmeHK%}N6?5T3ME(~8)GV2^w8R-yH$=s{R&J$MsV^q|u2xAACzZ;l$YgU%%PUeAWMQpvWW*613?pL1-DY4W=3~f+BP&qAnHYi6L}3`Yn@Z8%#Coau8}}9LLNo%nL=R z+0k#QbP%>iZkYjQV48uf?iT6(Kl%CnKV8H