mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-09-13 07:33:42 +08:00
add 新增 warmflow 源码级定制化开发
This commit is contained in:
parent
4d1448b4b4
commit
ad017ca4da
14
pom.xml
14
pom.xml
@ -62,7 +62,6 @@
|
||||
<!-- SMS 配置 -->
|
||||
<sms4j.version>3.3.5</sms4j.version>
|
||||
<!-- 工作流配置 -->
|
||||
<warm-flow.version>1.8.9</warm-flow.version>
|
||||
<liteflow.version>2.16.1.2</liteflow.version>
|
||||
<!-- mqtt客户端 -->
|
||||
<mica-mqtt.version>2.6.8</mica-mqtt.version>
|
||||
@ -346,19 +345,6 @@
|
||||
<version>${mapstruct-plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Warm-Flow国产工作流引擎, 在线文档:http://warm-flow.cn/ -->
|
||||
<dependency>
|
||||
<groupId>org.dromara.warm</groupId>
|
||||
<artifactId>warm-flow-mybatis-plus-sb4-starter</artifactId>
|
||||
<version>${warm-flow.version}</version>
|
||||
</dependency>
|
||||
<!-- Warm-Flow UI 插件 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara.warm</groupId>
|
||||
<artifactId>warm-flow-plugin-ui-sb-web</artifactId>
|
||||
<version>${warm-flow.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- LiteFlow 规则编排引擎 -->
|
||||
<dependency>
|
||||
<groupId>com.yomahub</groupId>
|
||||
|
||||
@ -69,4 +69,18 @@ public final class SpringUtils extends SpringUtil {
|
||||
return Threading.VIRTUAL.isActive(getBean(Environment.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Bean 容器中不存在时返回null而非抛出异常
|
||||
*
|
||||
* @param clazz Bean类型
|
||||
* @return Bean实例 不存在时返回null
|
||||
*/
|
||||
public static <T> T getBeanOrNull(Class<T> clazz) {
|
||||
try {
|
||||
return getBean(clazz);
|
||||
} catch (NoSuchBeanDefinitionException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -84,18 +84,6 @@
|
||||
<artifactId>ruoyi-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Warm-Flow 工作流引擎 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara.warm</groupId>
|
||||
<artifactId>warm-flow-mybatis-plus-sb4-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Warm-Flow UI 插件 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara.warm</groupId>
|
||||
<artifactId>warm-flow-plugin-ui-sb-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- LiteFlow 规则编排 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
|
||||
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import org.dromara.warm.flow.config.WarmFlowProperties;
|
||||
import org.dromara.warm.flow.handler.DataFillHandler;
|
||||
import org.dromara.warm.flow.handler.PermissionHandler;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.warm.flow.listener.GlobalListener;
|
||||
import org.dromara.warm.flow.service.*;
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
|
||||
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.function.Supplier;
|
||||
/**
|
||||
* 流程引擎,通过静态方法驱动流程流转
|
||||
*/
|
||||
public class FlowEngine {
|
||||
|
||||
private static WarmFlowProperties flowConfig;
|
||||
private static DataFillHandler dataFillHandler;
|
||||
private static PermissionHandler permissionHandler;
|
||||
private static GlobalListener globalListener;
|
||||
|
||||
public static DefService defService() {
|
||||
return SpringUtils.getBeanOrNull(DefService.class);
|
||||
}
|
||||
|
||||
public static NodeService nodeService() {
|
||||
return SpringUtils.getBeanOrNull(NodeService.class);
|
||||
}
|
||||
|
||||
public static SkipService skipService() {
|
||||
return SpringUtils.getBeanOrNull(SkipService.class);
|
||||
}
|
||||
|
||||
public static InsService insService() {
|
||||
return SpringUtils.getBeanOrNull(InsService.class);
|
||||
}
|
||||
|
||||
public static TaskService taskService() {
|
||||
return SpringUtils.getBeanOrNull(TaskService.class);
|
||||
}
|
||||
|
||||
public static HisTaskService hisTaskService() {
|
||||
return SpringUtils.getBeanOrNull(HisTaskService.class);
|
||||
}
|
||||
|
||||
public static UserService userService() {
|
||||
return SpringUtils.getBeanOrNull(UserService.class);
|
||||
}
|
||||
|
||||
public static ChartService chartService() {
|
||||
return SpringUtils.getBeanOrNull(ChartService.class);
|
||||
}
|
||||
|
||||
public static WarmFlowProperties getFlowConfig() {
|
||||
return flowConfig;
|
||||
}
|
||||
|
||||
public static void setFlowConfig(WarmFlowProperties flowConfig) {
|
||||
FlowEngine.flowConfig = flowConfig;
|
||||
}
|
||||
|
||||
public static void initDataFillHandler(String handlerPath) {
|
||||
dataFillHandler = initBean(DataFillHandler.class, handlerPath, () -> new DataFillHandler() {});
|
||||
}
|
||||
|
||||
public static void initPermissionHandler(String handlerPath) {
|
||||
permissionHandler = initBean(PermissionHandler.class, handlerPath, null);
|
||||
}
|
||||
|
||||
public static void initGlobalListener(String handlerPath) {
|
||||
globalListener = initBean(GlobalListener.class, handlerPath, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取填充类
|
||||
*/
|
||||
public static DataFillHandler dataFillHandler() {
|
||||
return dataFillHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取填充类
|
||||
*/
|
||||
public static PermissionHandler permissionHandler() {
|
||||
return permissionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全局监听器
|
||||
*/
|
||||
public static GlobalListener globalListener() {
|
||||
return globalListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库类型
|
||||
*/
|
||||
public static String dataSourceType() {
|
||||
return flowConfig.getDataSourceType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化bean,先从yml配置获取bean的全包名路径,否则从spring容器获取bean,如果都没有,则通过supplier获取bean
|
||||
*
|
||||
* @param tClazz bean的class类型
|
||||
* @param beanPath bean全包名路径
|
||||
* @param supplier 获取bean的lambda
|
||||
* @param <T> bean类型
|
||||
* @return bean
|
||||
*/
|
||||
private static <T> T initBean(Class<T> tClazz, String beanPath, Supplier<T> supplier) {
|
||||
T hander = null;
|
||||
try {
|
||||
if (!StrUtil.isEmpty(beanPath)) {
|
||||
Class<?> clazz = ClassUtil.loadClass(beanPath);
|
||||
if (clazz != null && tClazz.isAssignableFrom(clazz)) {
|
||||
Constructor<?> constructor = clazz.getConstructor();
|
||||
hander = tClazz.cast(constructor.newInstance());
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
if (hander == null) {
|
||||
hander = SpringUtils.getBeanOrNull(tClazz);
|
||||
}
|
||||
if (hander == null && supplier != null) {
|
||||
hander = supplier.get();
|
||||
}
|
||||
return hander;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.config;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.enums.FrameworkType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 工作流引擎初始化,各服务由注解扫描注册(见 flow.service 包的 @Service)
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023/6/5 23:01
|
||||
*/
|
||||
@Component
|
||||
@EnableConfigurationProperties(WarmFlowProperties.class)
|
||||
@ConditionalOnProperty(value = "warm-flow.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class WarmFlowInitializer {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(WarmFlowInitializer.class);
|
||||
|
||||
private final WarmFlowProperties warmFlow;
|
||||
|
||||
public WarmFlowInitializer(WarmFlowProperties warmFlow) {
|
||||
this.warmFlow = warmFlow;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
warmFlow.init();
|
||||
warmFlow.setFramework(FrameworkType.SPRING_BOOT);
|
||||
FlowEngine.setFlowConfig(warmFlow);
|
||||
log.info("【warm-flow】,加载完成");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.enums.ChartStatus;
|
||||
import org.dromara.warm.flow.enums.FrameworkType;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
/**
|
||||
* WarmFlow属性配置文件(warm-flow 前缀绑定)
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ConfigurationProperties("warm-flow")
|
||||
public class WarmFlowProperties implements Serializable {
|
||||
|
||||
/**
|
||||
* 开关
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 框架类型: springboot、solon
|
||||
*/
|
||||
private FrameworkType framework;
|
||||
|
||||
/**
|
||||
* 启动banner
|
||||
*/
|
||||
private boolean banner = true;
|
||||
|
||||
/**
|
||||
* 数据填充处理类路径
|
||||
*/
|
||||
private String dataFillHandlerPath;
|
||||
|
||||
/**
|
||||
* 办理人权限处理器类路径
|
||||
*/
|
||||
private String permissionHandlerPath;
|
||||
|
||||
/**
|
||||
* 全局监听器类路径
|
||||
*/
|
||||
private String globalListenerPath;
|
||||
|
||||
/**
|
||||
* 数据源类型, mybatis模块对orm进一步的封装, 由于各数据库分页语句存在差异,
|
||||
* 当配置此参数时, 以此参数结果为基准, 未配置时, 取DataSource中数据源类型,
|
||||
* 兜底为mysql数据库
|
||||
*/
|
||||
private String dataSourceType;
|
||||
|
||||
/**
|
||||
* ui开关
|
||||
*/
|
||||
private boolean ui = true;
|
||||
|
||||
/**
|
||||
* 如果需要工作流共享业务系统权限,默认Authorization,如果有多个token,用逗号分隔
|
||||
*/
|
||||
private String tokenName = "Authorization";
|
||||
|
||||
/**
|
||||
* 公共模型流程状态对应的三原色
|
||||
*/
|
||||
private List<String> chartStatusColor;
|
||||
|
||||
/**
|
||||
* 经典模式流程状态对应的三原色
|
||||
*/
|
||||
private List<String> chartStatusColorClassics;
|
||||
|
||||
/**
|
||||
* 仿钉钉模式流程状态对应的三原色
|
||||
*/
|
||||
private List<String> chartStatusColorMimic;
|
||||
|
||||
/**
|
||||
* 是否显示流程图顶部文字
|
||||
*/
|
||||
private boolean topTextShow = true;
|
||||
|
||||
public void init() {;
|
||||
|
||||
// 设置数据填充处理类
|
||||
FlowEngine.initDataFillHandler(this.getDataFillHandlerPath());
|
||||
|
||||
// 设置办理人权限处理类
|
||||
FlowEngine.initPermissionHandler(this.getPermissionHandlerPath());
|
||||
|
||||
// 设置全局监听器
|
||||
FlowEngine.initGlobalListener(this.getGlobalListenerPath());
|
||||
|
||||
// 打印banner图
|
||||
printBanner();
|
||||
|
||||
// 初始化流程状态对应的自定义三原色
|
||||
ChartStatus.initCustomColor(this.getChartStatusColor(), this.getChartStatusColorClassics(), this.getChartStatusColorMimic());
|
||||
}
|
||||
|
||||
private void printBanner() {
|
||||
if (this.isBanner()) {
|
||||
System.out.println("\n" +
|
||||
" ▄ ▄ ▄▄▄▄▄▄ ▄ \n" +
|
||||
" █ █ █ ▄▄▄ ▄ ▄▄ ▄▄▄▄▄ █ █ ▄▄▄ ▄ ▄ \n" +
|
||||
" ▀ █▀█ █ ▀ █ █▀ ▀ █ █ █ ▄▄▄▄▄ █▄▄▄▄▄ █ █▀ ▀█ ▀▄ ▄ ▄▀ \n" +
|
||||
" ██ ██▀ ▄▀▀▀█ █ █ █ █ █ █ █ █ █▄█▄█ \n" +
|
||||
" █ █ ▀▄▄▀█ █ █ █ █ █ █▄▄ ▀█▄█▀ █ █ \n" +
|
||||
"\n" +
|
||||
"\033[32m :: Warm-Flow :: (v" + WarmFlowProperties.class.getPackage()
|
||||
.getImplementationVersion() + ")\033[0m\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.constant;
|
||||
|
||||
/**
|
||||
* 工作流中用到的一些常量
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023/3/30 14:05
|
||||
*/
|
||||
public class ExceptionCons {
|
||||
|
||||
public static final String SAME_CONDITION_VALUE = "中间节点,同一个节点不能有相同跳转类型,跳转同一个目标节点!";
|
||||
|
||||
public static final String SAME_CONDITION_NODE = "互斥网关,同一个节点不能有相同跳转条件,跳转同一个目标节点!";
|
||||
|
||||
public static final String SAME_DEST_NODE = "并行网关,同一个节点不能跳转同一个目标节点!";
|
||||
|
||||
public static final String MUL_START_NODE = "开始节点不能超过1个!";
|
||||
|
||||
public static final String MUL_SKIP_BETWEEN = "不可同时通过或者退回到多个中间节点,必须先流转到网关节点!";
|
||||
|
||||
public static final String MUL_START_SKIP = "节点流转条件不能超过1个!";
|
||||
|
||||
public static final String LOST_START_NODE = "流程缺少开始节点!";
|
||||
|
||||
public static final String LOST_NODE_CODE = "节点编码缺失";
|
||||
|
||||
public static final String MUST_SKIP = "开始或者中间节点必须画跳转线";
|
||||
|
||||
public static final String SAME_NODE_CODE = "同一流程中节点编码重复!";
|
||||
|
||||
public static final String NULL_DEST_NODE = "无法他跳转,未配置目标节点!";
|
||||
|
||||
public static final String NULL_SKIP_TYPE = "未找到跳转类型匹配的目标节点!";
|
||||
|
||||
public static final String NULL_CONDITION_VALUE_NODE = "未找到跳转条件,不支持跳转!";
|
||||
|
||||
public static final String NULL_CONDITION_VALUE = "跳转条件不能为空!";
|
||||
|
||||
public static final String FIRST_FORBID_BACK = "禁止退回到第一个节点";
|
||||
|
||||
public static final String NULL_ROLE_NODE = "无法跳转到该节点,请检查当前用户是否有权限!";
|
||||
|
||||
public static final String LOST_DEST_NODE = "目标节点为空!";
|
||||
|
||||
public static final String LOST_CUR_NODE = "当前流程节点丢失!";
|
||||
|
||||
public static final String NULL_NODE_CODE = "目标节点编码不存在!";
|
||||
|
||||
public static final String NULL_BUSINESS_ID = "业务id为空!";
|
||||
|
||||
public static final String NULL_FLOW_CODE = "流程编码缺失!";
|
||||
|
||||
public static final String NOT_FOUNT_DEF = "流程定义不存在!";
|
||||
|
||||
public static final String NOT_FOUNT_INSTANCE = "流程实例获取失败!";
|
||||
|
||||
public static final String NULL_INSTANCE_ID = "流程实例id不能为空!";
|
||||
|
||||
public static final String NULL_TASK_ID = "任务id不能为空!";
|
||||
|
||||
public static final String NOT_FOUNT_TASK = "未找到待办任务!";
|
||||
|
||||
public static final String TASK_NOT_ONE = "此接口不能同时跳转多个待办任务,请更换!";
|
||||
|
||||
public static final String NOT_DEFINITION_ID = "流程定义id不能为空!";
|
||||
|
||||
public static final String NOT_NODE_DATA = "流程节点数据缺失!";
|
||||
|
||||
public static final String EXIST_START_TASK = "流程定义已开启过审批任务,不可操作!";
|
||||
|
||||
public static final String FLOW_FINISH = "流程已完成!";
|
||||
|
||||
public static final String NOT_AUTHORITY = "请检查当前用户是否有权限!";
|
||||
|
||||
public static final String SIGN_NULL_HANDLER = "会签票签时,办理人标识不能为空";
|
||||
|
||||
public static final String REDUCTION_SIGN_ONE_ERROR = "办理人不足或者只有一人,不可减签";
|
||||
|
||||
public static final String IS_ALREADY_SIGN = "已经是待办人,不可加签";
|
||||
|
||||
public static final String IS_ALREADY_TRANSFER = "已经是转办人,不可转办";
|
||||
|
||||
public static final String IS_ALREADY_DEPUTE = "已经是受托人,不可委托";
|
||||
|
||||
public static final String NOT_ACTIVITY = "当前流程定义或者实例已经挂起,请先激活";
|
||||
|
||||
public static final String NOT_DEFINITION_ACTIVITY = "当前流程定义已挂起,不可开启新的流程";
|
||||
|
||||
public static final String DEFINITION_ALREADY_ACTIVITY = "当前流程定义已经激活";
|
||||
|
||||
public static final String DEFINITION_ALREADY_SUSPENDED = "当前流程定义已经挂起";
|
||||
|
||||
public static final String INSTANCE_ALREADY_ACTIVITY = "当前流程实例已经激活";
|
||||
|
||||
public static final String INSTANCE_ALREADY_SUSPENDED = "当前流程实例已经挂起";
|
||||
|
||||
public static final String FORM_ALREADY_PUBLISH = "当前表单状态已发布";
|
||||
|
||||
public static final String FORM_ALREADY_UN_PUBLISH = "当前表单状态未发布";
|
||||
|
||||
public static final String FORM_NOT_ONE = "表单数据错误, 请联系管理员排查!";
|
||||
|
||||
public static final String ID_EMPTY = "ID不能为空";
|
||||
|
||||
public static final String NOT_DEF_PROMOTER_NOT_CANCEL = "不是当前流程的发起人,无法撤销";
|
||||
|
||||
public static final String HANDLER_NOT_EMPTY = "办理人不能为空";
|
||||
|
||||
public static final String TAR_NOT_GATEWAY = "目标节点不能是网关节点!";
|
||||
|
||||
public static final String NOT_FOUND_FLOW_TASK = "未获取到流程任务";
|
||||
|
||||
public static final String FLOW_HAVE_USELESS_SKIP = "存在无用的跳转";
|
||||
|
||||
public static final String NULL_TRANSFER_HANDLER = "转办对象不能为空";
|
||||
|
||||
public static final String NULL_DEPUTE_HANDLER = "委托对象不能为空";
|
||||
|
||||
public static final String NULL_ADD_SIGNATURE_HANDLER = "加签对象不能为空";
|
||||
|
||||
public static final String NULL_REDUCTION_SIGNATURE_HANDLER = "减签对象不能为空";
|
||||
|
||||
public static final String READ_IS_ERROR = "读取is流失败";
|
||||
|
||||
public static final String EXIST_USE_FORM = "流程表单已使用,不可操作!";
|
||||
|
||||
public static final String NOT_FOUNT_LAST_TASK = "未找到前置任务!";
|
||||
|
||||
public static final String NOT_FOUNT_HANDLED_TASK = "未找到您已办理过的任务!";
|
||||
|
||||
public static final String NOT_FOUNT_HANDLED_TASK_HANDLER = "拿回的任务未办理!";
|
||||
|
||||
public static final String START_NODE_NOT_ALLOW_JUMP = "开始节点不允许跳转!";
|
||||
|
||||
public static final String NOT_DRAW_FLOW_ERROR = "未绘制流程图,不可发布";
|
||||
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.constant;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* warm-flow常量
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public class FlowCons {
|
||||
|
||||
/**
|
||||
* 分隔符
|
||||
*/
|
||||
public static final String SPLIT_AT = "@@";
|
||||
public static final String SPLIT_VERTICAL = "\\|";
|
||||
public static final String DEFAULT = "default";
|
||||
public static final String SPEL = "spel";
|
||||
public static final String SNEL = "snel";
|
||||
|
||||
public static final Pattern LISTENER_PATTERN = Pattern.compile("^([^()]*)(.*)$");
|
||||
|
||||
/**
|
||||
* 权限标识中的发起人标识符,办理过程中进行替换
|
||||
*/
|
||||
public static final String WARMFLOWINITIATOR = "warmFlowInitiator";
|
||||
|
||||
/**
|
||||
* 监听器参数
|
||||
*/
|
||||
public static final String WARM_LISTENER_PARAM = "WarmListenerParam";
|
||||
|
||||
/**
|
||||
* 表单自定义状态
|
||||
* 内置表单
|
||||
*/
|
||||
public static final String FORM_CUSTOM_Y = "Y";
|
||||
/**
|
||||
* 外挂表单路径
|
||||
*/
|
||||
public static final String FORM_CUSTOM_N = "N";
|
||||
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
public static final String FORM_DATA = "formData";
|
||||
|
||||
|
||||
public static final String PREVIOUS = "previous";
|
||||
|
||||
public static final String SUFFIX = "suffix";
|
||||
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 响应信息主体
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class ApiResult<T> implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
public static final int SUCCESS = 200;
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
public static final int FAIL = 500;
|
||||
|
||||
private int code;
|
||||
|
||||
private String msg;
|
||||
|
||||
private T data;
|
||||
|
||||
public static <T> ApiResult<T> ok() {
|
||||
return restResult(null, SUCCESS, "操作成功");
|
||||
}
|
||||
|
||||
public static <T> ApiResult<T> ok(T data) {
|
||||
return restResult(data, SUCCESS, "操作成功");
|
||||
}
|
||||
|
||||
public static <T> ApiResult<T> ok(T data, String msg) {
|
||||
return restResult(data, SUCCESS, msg);
|
||||
}
|
||||
|
||||
public static <T> ApiResult<T> fail() {
|
||||
return restResult(null, FAIL, "操作失败");
|
||||
}
|
||||
|
||||
public static <T> ApiResult<T> fail(String msg) {
|
||||
return restResult(null, FAIL, msg);
|
||||
}
|
||||
|
||||
public static <T> ApiResult<T> fail(T data) {
|
||||
return restResult(data, FAIL, "操作失败");
|
||||
}
|
||||
|
||||
public static <T> ApiResult<T> fail(T data, String msg) {
|
||||
return restResult(data, FAIL, msg);
|
||||
}
|
||||
|
||||
public static <T> ApiResult<T> fail(int code, String msg) {
|
||||
return restResult(null, code, msg);
|
||||
}
|
||||
|
||||
private static <T> ApiResult<T> restResult(T data, int code, String msg) {
|
||||
ApiResult<T> apiResult = new ApiResult<>();
|
||||
apiResult.setCode(code);
|
||||
apiResult.setData(data);
|
||||
apiResult.setMsg(msg);
|
||||
return apiResult;
|
||||
}
|
||||
|
||||
public static <T> Boolean isError(ApiResult<T> ret) {
|
||||
return !isSuccess(ret);
|
||||
}
|
||||
|
||||
public static <T> Boolean isSuccess(ApiResult<T> ret) {
|
||||
return ApiResult.SUCCESS == ret.getCode();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.dto;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 流程图所需数据集合
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023/3/30 14:27
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class DefChart {
|
||||
/**
|
||||
* 流程图所需的流程定义
|
||||
*/
|
||||
private DefJson defJson = new DefJson();
|
||||
|
||||
/**
|
||||
* 流程图所需的流程节点
|
||||
*/
|
||||
private List<NodeJson> nodeJsonList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 流程图所需的流程跳转
|
||||
*/
|
||||
private List<SkipJson> skipJsonList = new ArrayList<>();
|
||||
|
||||
}
|
||||
@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.dto;
|
||||
import org.dromara.warm.flow.entity.*;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.entity.FlowDefinition;
|
||||
import org.dromara.warm.flow.entity.FlowInstance;
|
||||
import org.dromara.warm.flow.entity.FlowNode;
|
||||
import org.dromara.warm.flow.entity.FlowSkip;
|
||||
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 流程定义json对象
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@Accessors(chain = true)
|
||||
@ToString
|
||||
public class DefJson {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 流程编码
|
||||
*/
|
||||
private String flowCode;
|
||||
|
||||
/**
|
||||
* 流程名称
|
||||
*/
|
||||
private String flowName;
|
||||
|
||||
/**
|
||||
* 设计器模型(CLASSICS经典模型 MIMIC仿钉钉模型)
|
||||
*/
|
||||
private String modelValue;
|
||||
|
||||
/**
|
||||
* 流程类别
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 流程版本
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 是否发布(0未开启 1开启)
|
||||
*/
|
||||
private Integer isPublish;
|
||||
|
||||
/**
|
||||
* 审批表单是否自定义(Y=是 N=否)
|
||||
*/
|
||||
private String formCustom;
|
||||
|
||||
/**
|
||||
* 审批表单是否自定义(Y=是 N=否)
|
||||
*/
|
||||
private String formPath;
|
||||
|
||||
/**
|
||||
* 监听器类型
|
||||
*/
|
||||
private String listenerType;
|
||||
|
||||
/**
|
||||
* 监听器路径
|
||||
*/
|
||||
private String listenerPath;
|
||||
|
||||
/**
|
||||
* 实例对象
|
||||
*/
|
||||
private FlowInstance instance;
|
||||
|
||||
/**
|
||||
* 扩展字段,预留给业务系统使用
|
||||
*/
|
||||
private String ext;
|
||||
|
||||
/**
|
||||
* 扩展map,保存业务自定义扩展属性
|
||||
*/
|
||||
private Map<String, Object> extMap;
|
||||
|
||||
/**
|
||||
* 所有节点结合
|
||||
*/
|
||||
private List<NodeJson> nodeList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 流程状态对应的三原色
|
||||
*/
|
||||
private List<String> chartStatusColor;
|
||||
|
||||
/**
|
||||
* 顶部信息: 比如流程名称等
|
||||
*/
|
||||
private String topText;
|
||||
|
||||
/**
|
||||
* 顶部信息: 流程名称是否显示
|
||||
*/
|
||||
private boolean topTextShow;
|
||||
|
||||
private String createBy;
|
||||
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 流程类别
|
||||
*/
|
||||
private List<Tree> categoryList;
|
||||
|
||||
/**
|
||||
* 自定义表单的唯一标识:如formCode+version
|
||||
*/
|
||||
private List<Tree> formPathList;
|
||||
|
||||
|
||||
public String getModelValue() {
|
||||
if (StrUtil.isEmpty(modelValue)) {
|
||||
modelValue = "CLASSICS";
|
||||
}
|
||||
return modelValue;
|
||||
}
|
||||
|
||||
public static DefJson copyDef(FlowDefinition definition) {
|
||||
DefJson defJson = new DefJson()
|
||||
.setFlowCode(definition.getFlowCode())
|
||||
.setFlowName(definition.getFlowName())
|
||||
.setModelValue(definition.getModelValue())
|
||||
.setVersion(definition.getVersion())
|
||||
.setIsPublish(definition.getIsPublish())
|
||||
.setCategory(definition.getCategory())
|
||||
.setFormCustom(definition.getFormCustom())
|
||||
.setFormPath(definition.getFormPath())
|
||||
.setListenerType(definition.getListenerType())
|
||||
.setListenerPath(definition.getListenerPath())
|
||||
.setExt(definition.getExt())
|
||||
.setCreateBy(definition.getCreateBy())
|
||||
.setUpdateBy(definition.getUpdateBy());
|
||||
|
||||
List<NodeJson> nodeList = new ArrayList<>();
|
||||
defJson.setNodeList(nodeList);
|
||||
for (FlowNode node : definition.getNodeList()) {
|
||||
// 向节点中添加子节点
|
||||
NodeJson nodeJson = new NodeJson()
|
||||
.setNodeType(node.getNodeType())
|
||||
.setNodeCode(node.getNodeCode())
|
||||
.setNodeName(node.getNodeName())
|
||||
.setPermissionFlag(node.getPermissionFlag())
|
||||
.setNodeRatio(node.getNodeRatio())
|
||||
.setCoordinate(node.getCoordinate())
|
||||
.setAnyNodeSkip(node.getAnyNodeSkip())
|
||||
.setListenerType(node.getListenerType())
|
||||
.setListenerPath(node.getListenerPath())
|
||||
.setFormCustom(node.getFormCustom())
|
||||
.setFormPath(node.getFormPath())
|
||||
.setExt(node.getExt())
|
||||
.setCreateBy(node.getCreateBy())
|
||||
.setUpdateBy(node.getUpdateBy());
|
||||
nodeList.add(nodeJson);
|
||||
|
||||
List<SkipJson> skipList = new ArrayList<>();
|
||||
nodeJson.setSkipList(skipList);
|
||||
if (CollUtil.isNotEmpty(node.getSkipList())) {
|
||||
for (FlowSkip skip : node.getSkipList()) {
|
||||
skipList.add(new SkipJson()
|
||||
.setCoordinate(skip.getCoordinate())
|
||||
.setSkipType(skip.getSkipType())
|
||||
.setSkipName(skip.getSkipName())
|
||||
.setSkipCondition(skip.getSkipCondition())
|
||||
.setNowNodeCode(skip.getNowNodeCode())
|
||||
.setNextNodeCode(skip.getNextNodeCode())
|
||||
.setCreateBy(skip.getCreateBy())
|
||||
.setUpdateBy(skip.getUpdateBy()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return defJson;
|
||||
}
|
||||
|
||||
public static FlowDefinition copyDef(DefJson defJson) {
|
||||
FlowDefinition definition = new FlowDefinition()
|
||||
.setId(defJson.getId())
|
||||
.setFlowCode(defJson.getFlowCode())
|
||||
.setFlowName(defJson.getFlowName())
|
||||
.setModelValue(defJson.getModelValue())
|
||||
.setVersion(defJson.getVersion())
|
||||
.setCategory(defJson.getCategory())
|
||||
.setFormCustom(defJson.getFormCustom())
|
||||
.setFormPath(defJson.getFormPath())
|
||||
.setListenerType(defJson.getListenerType())
|
||||
.setListenerPath(defJson.getListenerPath())
|
||||
.setExt(defJson.getExt())
|
||||
.setCreateBy(defJson.getCreateBy())
|
||||
.setUpdateBy(defJson.getUpdateBy());
|
||||
|
||||
List<FlowNode> nodeList = new ArrayList<>();
|
||||
definition.setNodeList(nodeList);
|
||||
for (NodeJson nodeJson : defJson.getNodeList()) {
|
||||
// 向节点中添加子节点
|
||||
FlowNode node = new FlowNode()
|
||||
.setNodeType(nodeJson.getNodeType())
|
||||
.setNodeCode(nodeJson.getNodeCode())
|
||||
.setNodeName(nodeJson.getNodeName())
|
||||
.setPermissionFlag(nodeJson.getPermissionFlag())
|
||||
.setNodeRatio(nodeJson.getNodeRatio() != null ? nodeJson.getNodeRatio() : "0")
|
||||
.setCoordinate(nodeJson.getCoordinate())
|
||||
.setAnyNodeSkip(nodeJson.getAnyNodeSkip())
|
||||
.setListenerType(nodeJson.getListenerType())
|
||||
.setListenerPath(nodeJson.getListenerPath())
|
||||
.setFormCustom(nodeJson.getFormCustom())
|
||||
.setFormPath(nodeJson.getFormPath())
|
||||
.setExt(nodeJson.getExt())
|
||||
.setCreateBy(nodeJson.getCreateBy())
|
||||
.setUpdateBy(nodeJson.getUpdateBy());
|
||||
nodeList.add(node);
|
||||
|
||||
List<FlowSkip> skipList = new ArrayList<>();
|
||||
node.setSkipList(skipList);
|
||||
|
||||
if (CollUtil.isNotEmpty(nodeJson.getSkipList())) {
|
||||
for (SkipJson skipJson : nodeJson.getSkipList()) {
|
||||
skipList.add(new FlowSkip()
|
||||
.setCoordinate(skipJson.getCoordinate())
|
||||
.setSkipType(skipJson.getSkipType())
|
||||
.setSkipName(skipJson.getSkipName())
|
||||
.setSkipCondition(skipJson.getSkipCondition())
|
||||
.setNowNodeCode(skipJson.getNowNodeCode())
|
||||
.setNextNodeCode(skipJson.getNextNodeCode())
|
||||
.setCreateBy(skipJson.getCreateBy())
|
||||
.setUpdateBy(skipJson.getUpdateBy()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return definition;
|
||||
}
|
||||
|
||||
public static FlowCombine copyCombine(DefJson defJson) {
|
||||
FlowDefinition definition = copyDef(defJson);
|
||||
FlowCombine flowCombine = new FlowCombine();
|
||||
flowCombine.setDefinition(definition);
|
||||
flowCombine.setAllNodes(definition.getNodeList());
|
||||
List<FlowSkip> skipList = definition.getNodeList().stream()
|
||||
.map(FlowNode::getSkipList)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(List::stream)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
flowCombine.setAllSkips(skipList);
|
||||
return flowCombine;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.dto;
|
||||
import org.dromara.warm.flow.entity.*;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.entity.FlowDefinition;
|
||||
import org.dromara.warm.flow.entity.FlowNode;
|
||||
import org.dromara.warm.flow.entity.FlowSkip;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 流程数据集合
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023/3/30 14:27
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FlowCombine {
|
||||
/**
|
||||
* 所有的流程定义
|
||||
*/
|
||||
private FlowDefinition definition = new FlowDefinition();
|
||||
|
||||
/**
|
||||
* 所有的流程节点
|
||||
*/
|
||||
private List<FlowNode> allNodes = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 所有的流程节点跳转关联
|
||||
*/
|
||||
private List<FlowSkip> allSkips = new ArrayList<>();
|
||||
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.dto;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author vanlin
|
||||
* @since 2024-9-24 11:11
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class FlowDto implements Serializable {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 数据
|
||||
*/
|
||||
private Object data;
|
||||
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 表格分页数据对象
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public class FlowPage<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 总记录数
|
||||
*/
|
||||
private long total;
|
||||
|
||||
/**
|
||||
* 列表数据
|
||||
*/
|
||||
private List<T> rows;
|
||||
|
||||
/**
|
||||
* 消息状态码
|
||||
*/
|
||||
private int code;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*
|
||||
* @param list 列表数据
|
||||
* @param total 总记录数
|
||||
*/
|
||||
public FlowPage(List<T> list, int total) {
|
||||
this.rows = list;
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.dto;
|
||||
|
||||
import org.dromara.warm.flow.json.JsonUtil;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.handler.PermissionHandler;
|
||||
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 工作流内置参数
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023/3/31 17:18
|
||||
*/
|
||||
public class FlowParams implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 流程编码
|
||||
*/
|
||||
@Getter
|
||||
private String flowCode;
|
||||
|
||||
/**
|
||||
* 当前办理人唯一标识:就是确定唯一用的,如用户id,通常用来入库,记录流程实例创建人,办理人
|
||||
*/
|
||||
private String handler;
|
||||
|
||||
/**
|
||||
* 节点编码(如果要指定跳转节点,传入)
|
||||
*/
|
||||
@Getter
|
||||
private String nodeCode;
|
||||
|
||||
/**
|
||||
* 用户权限标识:和办理权限有关,是否有办理权限,通俗来说,就是设计器里面预设的办理人,和这个标识是否有交集,有交集就可以办理,审批的时候,就不会提示报错
|
||||
*/
|
||||
private List<String> permissionFlag;
|
||||
|
||||
/**
|
||||
* 跳转类型(PASS审批通过 REJECT退回)
|
||||
*/
|
||||
@Getter
|
||||
private String skipType;
|
||||
|
||||
/**
|
||||
* 审批意见
|
||||
*/
|
||||
@Getter
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 流程变量
|
||||
*/
|
||||
@Getter
|
||||
private Map<String, Object> variable = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 流程实例状态
|
||||
*/
|
||||
@Getter
|
||||
private String flowStatus;
|
||||
|
||||
/**
|
||||
* 历史任务表状态
|
||||
*/
|
||||
@Getter
|
||||
private String hisStatus;
|
||||
|
||||
/**
|
||||
* 流程激活状态(0挂起 1激活)
|
||||
*/
|
||||
@Getter
|
||||
private Integer activityStatus;
|
||||
|
||||
/**
|
||||
* 协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)
|
||||
*/
|
||||
@Getter
|
||||
private Integer cooperateType;
|
||||
|
||||
/**
|
||||
* 扩展字段,预留给业务系统使用
|
||||
*/
|
||||
@Getter
|
||||
private String ext;
|
||||
|
||||
/**
|
||||
* 扩展字段,预留给业务系统使用
|
||||
*/
|
||||
@Getter
|
||||
private String hisTaskExt;
|
||||
|
||||
/**
|
||||
* 增加办理人:加签,转办,委托
|
||||
*/
|
||||
@Getter
|
||||
private List<String> addHandlers;
|
||||
|
||||
/**
|
||||
* 减少办理人:减签,委托
|
||||
*/
|
||||
@Getter
|
||||
private List<String> reductionHandlers;
|
||||
|
||||
/**
|
||||
* 忽略-办理权限校验(true:忽略,false:不忽略)
|
||||
*/
|
||||
@Getter
|
||||
private boolean ignore;
|
||||
|
||||
/**
|
||||
* 忽略-委派处理(true:忽略,false:不忽略)
|
||||
*/
|
||||
@Getter
|
||||
private boolean ignoreDepute;
|
||||
|
||||
/**
|
||||
* 忽略-会签票签处理(true:忽略,false:不忽略)
|
||||
*/
|
||||
@Getter
|
||||
private boolean ignoreCooperate;
|
||||
|
||||
/**
|
||||
* 执行的下个任务的办理人
|
||||
*/
|
||||
@Getter
|
||||
private String[] nextHandler;
|
||||
|
||||
/**
|
||||
* 下个任务处理人配置类型(true-追加,false-覆盖,默认false)
|
||||
*/
|
||||
@Getter
|
||||
private boolean nextHandlerAppend;
|
||||
|
||||
public FlowParams() {
|
||||
}
|
||||
|
||||
public FlowParams(String skipType, String message, Map<String, Object> variable) {
|
||||
this.skipType = skipType;
|
||||
this.message = message;
|
||||
this.variable = variable;
|
||||
}
|
||||
|
||||
public FlowParams(String nodeCode, String skipType, String message, Map<String, Object> variable) {
|
||||
this.nodeCode = nodeCode;
|
||||
this.skipType = skipType;
|
||||
this.message = message;
|
||||
this.variable = variable;
|
||||
}
|
||||
|
||||
public FlowParams(String skipType, String message, Map<String, Object> variable
|
||||
, String flowStatus, String hisStatus) {
|
||||
this.skipType = skipType;
|
||||
this.message = message;
|
||||
this.variable = variable;
|
||||
this.flowStatus = flowStatus;
|
||||
this.hisStatus = hisStatus;
|
||||
}
|
||||
|
||||
public FlowParams(String nodeCode, String skipType, String message, Map<String, Object> variable
|
||||
, String flowStatus, String hisStatus) {
|
||||
this.nodeCode = nodeCode;
|
||||
this.skipType = skipType;
|
||||
this.message = message;
|
||||
this.variable = variable;
|
||||
this.flowStatus = flowStatus;
|
||||
this.hisStatus = hisStatus;
|
||||
}
|
||||
|
||||
public static FlowParams build() {
|
||||
return new FlowParams();
|
||||
}
|
||||
|
||||
public FlowParams flowCode(String flowCode) {
|
||||
this.flowCode = flowCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams handler(String handler) {
|
||||
this.handler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams nodeCode(String nodeCode) {
|
||||
this.nodeCode = nodeCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams permissionFlag(List<String> permissionFlag) {
|
||||
this.permissionFlag = permissionFlag;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams message(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams variable(Map<String, Object> variable) {
|
||||
this.variable = variable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams flowStatus(String flowStatus) {
|
||||
this.flowStatus = flowStatus;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams hisStatus(String hisStatus) {
|
||||
this.hisStatus = hisStatus;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams activityStatus(Integer activityStatus) {
|
||||
this.activityStatus = activityStatus;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams cooperateType(Integer cooperateType) {
|
||||
this.cooperateType = cooperateType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams ext(String ext) {
|
||||
this.ext = ext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams hisTaskExt(String hisTaskExt) {
|
||||
this.hisTaskExt = hisTaskExt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams nextHandler(String... nextHandler) {
|
||||
// 如果外部传递 null , 就忽略 , 维持节点本身审批,防止 null 数据变成 [null]
|
||||
if (nextHandler == null) {
|
||||
return this;
|
||||
}
|
||||
this.nextHandler = Arrays.stream(nextHandler).filter(Objects::nonNull).toArray(String[]::new);
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getVariableStr() {
|
||||
return JsonUtil.objToStr(variable);
|
||||
}
|
||||
|
||||
public String getHandler() {
|
||||
if (StrUtil.isEmpty(handler)) {
|
||||
PermissionHandler permissionHandler = FlowEngine.permissionHandler();
|
||||
if (permissionHandler != null) {
|
||||
try {
|
||||
handler = permissionHandler.getHandler();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
public List<String> getPermissionFlag() {
|
||||
if (CollUtil.isEmpty(permissionFlag)) {
|
||||
PermissionHandler permissionHandler = FlowEngine.permissionHandler();
|
||||
if (permissionHandler != null) {
|
||||
try {
|
||||
permissionFlag = permissionHandler.permissions();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return permissionFlag;
|
||||
}
|
||||
|
||||
public FlowParams skipType(String skipType) {
|
||||
this.skipType = skipType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams addHandlers(List<String> addHandlers) {
|
||||
this.addHandlers = addHandlers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams reductionHandlers(List<String> reductionHandlers) {
|
||||
this.reductionHandlers = reductionHandlers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams ignore(boolean ignore) {
|
||||
this.ignore = ignore;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams ignoreDepute(boolean ignoreDepute) {
|
||||
this.ignoreDepute = ignoreDepute;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams ignoreCooperate(boolean ignoreCooperate) {
|
||||
this.ignoreCooperate = ignoreCooperate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams nextHandlerAppend(boolean nextHandlerAppend) {
|
||||
this.nextHandlerAppend = nextHandlerAppend;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams formData(Map<String, Object> formData) {
|
||||
if (this.variable == null) {
|
||||
this.variable = new HashMap<>();
|
||||
}
|
||||
this.variable.put("formData", formData);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.warm.flow.utils.MapUtil;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 流程节点对象Vo
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@Accessors(chain = true)
|
||||
public class NodeJson {
|
||||
|
||||
/**
|
||||
* 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
|
||||
*/
|
||||
private Integer nodeType;
|
||||
/**
|
||||
* 流程节点编码 每个流程的nodeCode是唯一的,即definitionId+nodeCode唯一,在数据库层面做了控制
|
||||
*/
|
||||
private String nodeCode;
|
||||
/**
|
||||
* 流程节点名称
|
||||
*/
|
||||
private String nodeName;
|
||||
/**
|
||||
* 流程节点版本
|
||||
*/
|
||||
private String version;
|
||||
/**
|
||||
* 权限标识(权限类型:权限标识,可以多个,用@@隔开)
|
||||
*/
|
||||
private String permissionFlag;
|
||||
/**
|
||||
* 流程签署比例值
|
||||
*/
|
||||
private String nodeRatio;
|
||||
/**
|
||||
* 流程节点坐标
|
||||
*/
|
||||
private String coordinate;
|
||||
/**
|
||||
* 任意结点跳转
|
||||
*/
|
||||
private String anyNodeSkip;
|
||||
/**
|
||||
* 监听器类型
|
||||
*/
|
||||
private String listenerType;
|
||||
/**
|
||||
* 监听器路径
|
||||
*/
|
||||
private String listenerPath;
|
||||
/**
|
||||
* 审批表单是否自定义(Y=是 N=否)
|
||||
*/
|
||||
private String formCustom;
|
||||
|
||||
/**
|
||||
* 审批表单是否自定义(Y=是 N=否)
|
||||
*/
|
||||
private String formPath;
|
||||
|
||||
/**
|
||||
* 节点扩展属性
|
||||
*/
|
||||
private String ext;
|
||||
|
||||
/**
|
||||
* 办理状态: 0未办理 1待办理 2已办理
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 扩展map,保存业务自定义扩展属性
|
||||
*/
|
||||
private Map<String, Object> extMap;
|
||||
|
||||
/**
|
||||
* 流程图节点提示内容
|
||||
*/
|
||||
private PromptContent promptContent;
|
||||
|
||||
/**
|
||||
* 跳转条件
|
||||
*/
|
||||
private List<SkipJson> skipList = new ArrayList<>();
|
||||
|
||||
private String createBy;
|
||||
|
||||
private String updateBy;
|
||||
|
||||
public Map<String, Object> getExtMap() {
|
||||
if (MapUtil.isEmpty(extMap)) {
|
||||
extMap = new HashMap<>();
|
||||
}
|
||||
return extMap;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.dto;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.warm.flow.entity.FlowNode;
|
||||
import org.dromara.warm.flow.entity.FlowSkip;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 办理过程中途径数据,用于渲染流程图
|
||||
*
|
||||
* @author warm
|
||||
* @since 2025/1/4
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
public class PathWayData {
|
||||
/**
|
||||
* 流程定义id
|
||||
*/
|
||||
private Long defId;
|
||||
|
||||
/**
|
||||
* 流程实例id
|
||||
*/
|
||||
private Long insId;
|
||||
|
||||
/**
|
||||
* 跳转类型(PASS审批通过 REJECT退回)
|
||||
*/
|
||||
private String skipType;
|
||||
|
||||
/**
|
||||
* 目标结点集合
|
||||
*/
|
||||
private List<FlowNode> targetNodes = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 途径结点集合
|
||||
*/
|
||||
private List<FlowNode> pathWayNodes = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 途径流程跳转线
|
||||
*/
|
||||
private List<FlowSkip> pathWaySkips = new ArrayList<>();
|
||||
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 提示信息
|
||||
*
|
||||
* @author warm
|
||||
* @since 2025/6/5
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PromptContent {
|
||||
/**
|
||||
* 弹窗样式
|
||||
*/
|
||||
private Map<String, Object> dialogStyle;
|
||||
/**
|
||||
* 提示信息
|
||||
*/
|
||||
private List<InfoItem> info;
|
||||
|
||||
/**
|
||||
* 提示信息项
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class InfoItem {
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
private String prefix;
|
||||
/**
|
||||
* 前缀样式
|
||||
*/
|
||||
private Map<String, Object> prefixStyle;
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private String content;
|
||||
/**
|
||||
* 内容样式
|
||||
*/
|
||||
private Map<String, Object> contentStyle;
|
||||
/**
|
||||
* 行样式
|
||||
*/
|
||||
private Map<String, Object> rowStyle;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 节点跳转关联对象Vo
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@Accessors(chain = true)
|
||||
public class SkipJson {
|
||||
|
||||
/**
|
||||
* 当前流程节点的编码
|
||||
*/
|
||||
private String nowNodeCode;
|
||||
|
||||
/**
|
||||
* 下一个流程节点的编码
|
||||
*/
|
||||
private String nextNodeCode;
|
||||
|
||||
/**
|
||||
* 跳转名称
|
||||
*/
|
||||
private String skipName;
|
||||
|
||||
/**
|
||||
* 跳转类型(PASS审批通过 REJECT退回)
|
||||
*/
|
||||
private String skipType;
|
||||
|
||||
/**
|
||||
* 跳转条件
|
||||
*/
|
||||
private String skipCondition;
|
||||
|
||||
/**
|
||||
* 流程跳转坐标
|
||||
*/
|
||||
private String coordinate;
|
||||
|
||||
/**
|
||||
* 办理状态: 0未办理 1待办理 2已办理
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 扩展map,保存业务自定义扩展属性
|
||||
*/
|
||||
private Map<String, Object> extMap;
|
||||
|
||||
/**
|
||||
* 流程图节点提示内容
|
||||
*/
|
||||
private List<String> promptContent;
|
||||
|
||||
private String createBy;
|
||||
|
||||
private String updateBy;
|
||||
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 页面树列表
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Tree implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 父ID
|
||||
*/
|
||||
private String parentId;
|
||||
|
||||
/**
|
||||
* 子
|
||||
*/
|
||||
private List<Tree> children = new ArrayList<>();
|
||||
|
||||
}
|
||||
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.entity;
|
||||
import org.dromara.warm.flow.entity.RootEntity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.warm.flow.entity.FlowDefinition;
|
||||
import org.dromara.warm.flow.entity.FlowNode;
|
||||
import org.dromara.warm.flow.entity.FlowUser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
/**
|
||||
* 流程定义
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class FlowDefinition implements RootEntity {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
*
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 流程编码
|
||||
*/
|
||||
private String flowCode;
|
||||
|
||||
/**
|
||||
* 流程名称
|
||||
*/
|
||||
private String flowName;
|
||||
|
||||
/**
|
||||
* 设计器模型(CLASSICS经典模型 MIMIC仿钉钉模型)
|
||||
*/
|
||||
private String modelValue;
|
||||
|
||||
/**
|
||||
* 流程类别
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 流程版本
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 是否发布(0未开启 1开启)
|
||||
*/
|
||||
private Integer isPublish;
|
||||
|
||||
/**
|
||||
* 审批表单是否自定义(Y是 N否)
|
||||
*/
|
||||
private String formCustom;
|
||||
|
||||
/**
|
||||
* 审批表单路径
|
||||
*/
|
||||
private String formPath;
|
||||
|
||||
/**
|
||||
* 流程激活状态(0挂起 1激活)
|
||||
*/
|
||||
private Integer activityStatus;
|
||||
|
||||
/**
|
||||
* 监听器类型
|
||||
*/
|
||||
private String listenerType;
|
||||
|
||||
/**
|
||||
* 监听器路径
|
||||
*/
|
||||
private String listenerPath;
|
||||
|
||||
/**
|
||||
* 扩展字段,预留给业务系统使用
|
||||
*/
|
||||
private String ext;
|
||||
|
||||
@TableField(exist = false)
|
||||
private List<FlowNode> nodeList = new ArrayList<>();
|
||||
|
||||
@TableField(exist = false)
|
||||
private List<FlowUser> userList = new ArrayList<>();
|
||||
|
||||
|
||||
/**
|
||||
* 复制当前对象(不含主键、时间字段)
|
||||
*/
|
||||
public FlowDefinition copy() {
|
||||
return new FlowDefinition()
|
||||
.setDelFlag(this.getDelFlag())
|
||||
.setFlowCode(this.getFlowCode())
|
||||
.setFlowName(this.getFlowName())
|
||||
.setModelValue(this.getModelValue())
|
||||
.setCategory(this.getCategory())
|
||||
.setVersion(this.getVersion())
|
||||
.setFormCustom(this.getFormCustom())
|
||||
.setFormPath(this.getFormPath())
|
||||
.setListenerType(this.getListenerType())
|
||||
.setListenerPath(this.getListenerPath())
|
||||
.setExt(this.getExt())
|
||||
.setCreateBy(this.getCreateBy())
|
||||
.setUpdateBy(this.getUpdateBy());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.entity;
|
||||
import org.dromara.warm.flow.json.JsonUtil;
|
||||
|
||||
import org.dromara.warm.flow.entity.RootEntity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.warm.flow.entity.FlowHisTask;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
/**
|
||||
* 历史任务记录
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class FlowHisTask implements RootEntity {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 对应flow_definition表的id
|
||||
*/
|
||||
private Long definitionId;
|
||||
|
||||
/**
|
||||
* 流程名称
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String flowName;
|
||||
|
||||
/**
|
||||
* 流程实例表id
|
||||
*/
|
||||
private Long instanceId;
|
||||
|
||||
/**
|
||||
* 任务表id
|
||||
*/
|
||||
private Long taskId;
|
||||
|
||||
/**
|
||||
* 协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)
|
||||
*/
|
||||
private Integer cooperateType;
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String businessId;
|
||||
|
||||
/**
|
||||
* 开始节点编码
|
||||
*/
|
||||
private String nodeCode;
|
||||
|
||||
/**
|
||||
* 开始节点名称
|
||||
*/
|
||||
private String nodeName;
|
||||
|
||||
/**
|
||||
* 开始节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
|
||||
*/
|
||||
private Integer nodeType;
|
||||
|
||||
/**
|
||||
* 目标节点编码
|
||||
*/
|
||||
private String targetNodeCode;
|
||||
|
||||
/**
|
||||
* 结束节点名称
|
||||
*/
|
||||
private String targetNodeName;
|
||||
|
||||
/**
|
||||
* 审批者
|
||||
*/
|
||||
private String approver;
|
||||
|
||||
/**
|
||||
* 协作人(只有转办、会签、票签、委派)
|
||||
*/
|
||||
private String collaborator;
|
||||
|
||||
/**
|
||||
* 权限标识 permissionFlag的list形式
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<String> permissionList;
|
||||
|
||||
/**
|
||||
* 跳转类型(PASS通过 REJECT退回 NONE无动作)
|
||||
*/
|
||||
private String skipType;
|
||||
|
||||
/**
|
||||
* 流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)
|
||||
*/
|
||||
private String flowStatus;
|
||||
|
||||
/**
|
||||
* 审批意见
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 流程变量
|
||||
*/
|
||||
private String variable;
|
||||
|
||||
/**
|
||||
* 业务详情 存业务类的json
|
||||
*/
|
||||
private String ext;
|
||||
|
||||
/**
|
||||
* 审批表单是否自定义(Y=是 N=否)
|
||||
*/
|
||||
private String formCustom;
|
||||
|
||||
/**
|
||||
* 审批表单路径
|
||||
*/
|
||||
private String formPath;
|
||||
|
||||
|
||||
/**
|
||||
* 流程变量转 map
|
||||
*/
|
||||
public java.util.Map<String, Object> getVariableMap() {
|
||||
return JsonUtil.strToMap(getVariable());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.entity;
|
||||
import org.dromara.warm.flow.json.JsonUtil;
|
||||
|
||||
import org.dromara.warm.flow.entity.RootEntity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.warm.flow.entity.FlowInstance;
|
||||
|
||||
import java.util.Date;
|
||||
/**
|
||||
* 流程实例
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class FlowInstance implements RootEntity {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 对应flow_definition表的id
|
||||
*/
|
||||
private Long definitionId;
|
||||
|
||||
/**
|
||||
* 流程名称
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String flowName;
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
private String businessId;
|
||||
|
||||
/**
|
||||
* 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
|
||||
*/
|
||||
private Integer nodeType;
|
||||
|
||||
/**
|
||||
* 流程节点编码 每个流程的nodeCode是唯一的,即definitionId+nodeCode唯一,在数据库层面做了控制
|
||||
*/
|
||||
private String nodeCode;
|
||||
|
||||
/**
|
||||
* 流程节点名称
|
||||
*/
|
||||
private String nodeName;
|
||||
|
||||
/**
|
||||
* 流程变量
|
||||
*/
|
||||
private String variable;
|
||||
|
||||
/**
|
||||
* 流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)
|
||||
*/
|
||||
private String flowStatus;
|
||||
|
||||
/**
|
||||
* 流程激活状态(0挂起 1激活)
|
||||
*/
|
||||
private Integer activityStatus;
|
||||
|
||||
/**
|
||||
* 审批表单是否自定义(Y=是 N=否)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String formCustom;
|
||||
|
||||
/**
|
||||
* 审批表单是否自定义(Y=是 N=否)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String formPath;
|
||||
|
||||
/**
|
||||
* 流程定义json
|
||||
*/
|
||||
private String defJson;
|
||||
|
||||
/**
|
||||
* 扩展字段,预留给业务系统使用
|
||||
*/
|
||||
private String ext;
|
||||
|
||||
|
||||
/**
|
||||
* 流程变量转 map
|
||||
*/
|
||||
public java.util.Map<String, Object> getVariableMap() {
|
||||
return JsonUtil.strToMap(getVariable());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.entity;
|
||||
import org.dromara.warm.flow.entity.RootEntity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.warm.flow.entity.FlowNode;
|
||||
import org.dromara.warm.flow.entity.FlowSkip;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
/**
|
||||
* 流程节点
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class FlowNode implements RootEntity {
|
||||
|
||||
/**
|
||||
* 节点跳转列表(非表字段)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
List<FlowSkip> skipList;
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
|
||||
*/
|
||||
private Integer nodeType;
|
||||
/**
|
||||
* 流程id
|
||||
*/
|
||||
private Long definitionId;
|
||||
/**
|
||||
* 流程节点编码 每个流程的nodeCode是唯一的,即definitionId+nodeCode唯一,在数据库层面做了控制
|
||||
*/
|
||||
private String nodeCode;
|
||||
/**
|
||||
* 流程节点名称
|
||||
*/
|
||||
private String nodeName;
|
||||
/**
|
||||
* 权限标识(权限类型:权限标识,可以多个,用@@隔开)
|
||||
*/
|
||||
private String permissionFlag;
|
||||
/**
|
||||
* 流程签署比例值
|
||||
*/
|
||||
private String nodeRatio;
|
||||
/**
|
||||
* 流程节点坐标
|
||||
*/
|
||||
private String coordinate;
|
||||
/**
|
||||
* 版本
|
||||
*
|
||||
* @deprecated 下个版本废弃
|
||||
*/
|
||||
@Deprecated
|
||||
private String version;
|
||||
/**
|
||||
* 任意结点跳转
|
||||
*/
|
||||
private String anyNodeSkip;
|
||||
/**
|
||||
* 监听器类型
|
||||
*/
|
||||
private String listenerType;
|
||||
/**
|
||||
* 监听器路径
|
||||
*/
|
||||
private String listenerPath;
|
||||
/**
|
||||
* 审批表单是否自定义(Y=是 N=否)
|
||||
*/
|
||||
private String formCustom;
|
||||
|
||||
/**
|
||||
* 审批表单路径
|
||||
*/
|
||||
private String formPath;
|
||||
|
||||
/**
|
||||
* 节点扩展属性
|
||||
*/
|
||||
private String ext;
|
||||
|
||||
|
||||
/**
|
||||
* 复制当前对象(不含主键、时间字段)
|
||||
*/
|
||||
public FlowNode copy() {
|
||||
return new FlowNode()
|
||||
.setDelFlag(this.getDelFlag())
|
||||
.setNodeType(this.getNodeType())
|
||||
.setDefinitionId(this.getDefinitionId())
|
||||
.setNodeCode(this.getNodeCode())
|
||||
.setNodeName(this.getNodeName())
|
||||
.setNodeRatio(this.getNodeRatio())
|
||||
.setPermissionFlag(this.getPermissionFlag())
|
||||
.setCoordinate(this.getCoordinate())
|
||||
.setVersion(this.getVersion())
|
||||
.setAnyNodeSkip(this.getAnyNodeSkip())
|
||||
.setListenerType(this.getListenerType())
|
||||
.setListenerPath(this.getListenerPath())
|
||||
.setFormCustom(this.getFormCustom())
|
||||
.setFormPath(this.getFormPath())
|
||||
.setExt(this.getExt());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.entity;
|
||||
import org.dromara.warm.flow.entity.RootEntity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.warm.flow.entity.FlowSkip;
|
||||
|
||||
import java.util.Date;
|
||||
/**
|
||||
* 节点跳转关联
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class FlowSkip implements RootEntity {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 流程id
|
||||
*/
|
||||
private Long definitionId;
|
||||
|
||||
/**
|
||||
* 节点id
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private Long nodeId;
|
||||
|
||||
/**
|
||||
* 当前流程节点的编码
|
||||
*/
|
||||
private String nowNodeCode;
|
||||
|
||||
/**
|
||||
* 当前节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
|
||||
*/
|
||||
private Integer nowNodeType;
|
||||
|
||||
/**
|
||||
* 下一个流程节点的编码
|
||||
*/
|
||||
private String nextNodeCode;
|
||||
|
||||
/**
|
||||
* 下一个节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
|
||||
*/
|
||||
private Integer nextNodeType;
|
||||
|
||||
/**
|
||||
* 跳转名称
|
||||
*/
|
||||
private String skipName;
|
||||
|
||||
/**
|
||||
* 跳转类型(PASS审批通过 REJECT退回)
|
||||
*/
|
||||
private String skipType;
|
||||
|
||||
/**
|
||||
* 跳转条件
|
||||
*/
|
||||
private String skipCondition;
|
||||
|
||||
/**
|
||||
* 流程跳转坐标
|
||||
*/
|
||||
private String coordinate;
|
||||
|
||||
|
||||
/**
|
||||
* 复制当前对象(不含主键、时间字段)
|
||||
*/
|
||||
public FlowSkip copy() {
|
||||
return new FlowSkip()
|
||||
.setDelFlag(getDelFlag())
|
||||
.setDefinitionId(getDefinitionId())
|
||||
.setNowNodeCode(getNowNodeCode())
|
||||
.setNowNodeType(getNowNodeType())
|
||||
.setNextNodeCode(getNextNodeCode())
|
||||
.setNextNodeType(getNextNodeType())
|
||||
.setSkipName(getSkipName())
|
||||
.setSkipType(getSkipType())
|
||||
.setSkipCondition(getSkipCondition())
|
||||
.setCoordinate(getCoordinate());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.entity;
|
||||
import org.dromara.warm.flow.entity.RootEntity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.warm.flow.entity.FlowTask;
|
||||
import org.dromara.warm.flow.entity.FlowUser;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
/**
|
||||
* 待办任务
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class FlowTask implements RootEntity {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 对应flow_definition表的id
|
||||
*/
|
||||
private Long definitionId;
|
||||
|
||||
/**
|
||||
* 流程实例表id
|
||||
*/
|
||||
private Long instanceId;
|
||||
|
||||
/**
|
||||
* 流程名称
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String flowName;
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String businessId;
|
||||
|
||||
/**
|
||||
* 节点编码
|
||||
*/
|
||||
private String nodeCode;
|
||||
|
||||
/**
|
||||
* 节点名称
|
||||
*/
|
||||
private String nodeName;
|
||||
|
||||
/**
|
||||
* 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
|
||||
*/
|
||||
private Integer nodeType;
|
||||
|
||||
/**
|
||||
* 流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)
|
||||
*/
|
||||
private String flowStatus;
|
||||
|
||||
/**
|
||||
* 权限标识 permissionFlag的list形式
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<String> permissionList;
|
||||
|
||||
/**
|
||||
* 流程用户列表
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<FlowUser> userList;
|
||||
|
||||
/**
|
||||
* 审批表单是否自定义(Y=是 N=否)
|
||||
*/
|
||||
private String formCustom;
|
||||
|
||||
/**
|
||||
* 审批表单
|
||||
*/
|
||||
private String formPath;
|
||||
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.entity;
|
||||
import org.dromara.warm.flow.entity.RootEntity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.warm.flow.entity.FlowUser;
|
||||
|
||||
import java.util.Date;
|
||||
/**
|
||||
* 流程用户
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class FlowUser implements RootEntity {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 人员类型(1待办任务的审批人权限 2待办任务的转办人权限 3待办任务的委托人权限)
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 权限人
|
||||
*/
|
||||
private String processedBy;
|
||||
|
||||
/**
|
||||
* 任务表id
|
||||
*/
|
||||
private Long associated;
|
||||
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 流程基础entity
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023/5/17 17:23
|
||||
*/
|
||||
public interface RootEntity extends Serializable {
|
||||
|
||||
Long getId();
|
||||
|
||||
RootEntity setId(Long id);
|
||||
|
||||
Date getCreateTime();
|
||||
|
||||
RootEntity setCreateTime(Date createTime);
|
||||
|
||||
Date getUpdateTime();
|
||||
|
||||
RootEntity setUpdateTime(Date updateTime);
|
||||
|
||||
default String getCreateBy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default RootEntity setCreateBy(String createBy) {
|
||||
return this;
|
||||
}
|
||||
|
||||
default String getUpdateBy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default RootEntity setUpdateBy(String updateBy) {
|
||||
return this;
|
||||
}
|
||||
|
||||
String getDelFlag();
|
||||
|
||||
RootEntity setDelFlag(String delFlag);
|
||||
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
/**
|
||||
* 激活状态
|
||||
*
|
||||
* @author warm
|
||||
* @since 2025/6/25
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ActivityStatus {
|
||||
|
||||
/**
|
||||
* 激活状态
|
||||
*/
|
||||
SUSPENDED(0, "挂起"),
|
||||
|
||||
ACTIVITY(1, "激活");
|
||||
|
||||
private final Integer key;
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* 判断流程是否激活
|
||||
*/
|
||||
public static Boolean isActivity(Integer key) {
|
||||
return ObjectUtil.isNotNull(key) && (ActivityStatus.ACTIVITY.getKey().equals(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断流程是否挂起
|
||||
*/
|
||||
public static Boolean isSuspended(Integer key) {
|
||||
return ObjectUtil.isNotNull(key) && (ActivityStatus.SUSPENDED.getKey().equals(key));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 流程图状态
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023/3/31 12:16
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ChartStatus {
|
||||
/**
|
||||
* 流程图状态
|
||||
*/
|
||||
NOT_DONE(0, "未办理", new Color(107,114,128)),
|
||||
|
||||
TO_DO(1, "待办理", new Color(245,158,11)),
|
||||
|
||||
DONE(2, "已办理", new Color(56,161,105));
|
||||
|
||||
private final Integer key;
|
||||
private final String value;
|
||||
private final Color color;
|
||||
|
||||
private static final Map<Integer, Color> CUSTOM_COLOR = new HashMap<>();
|
||||
private static final Map<Integer, Color> CUSTOM_COLOR_CLASSICS = new HashMap<>();
|
||||
private static final Map<Integer, Color> CUSTOM_COLOR_MIMIC = new HashMap<>();
|
||||
|
||||
public static void initCustomColor(List<String> chartStatusColor, List<String> chartStatusColorClassics,
|
||||
List<String> chartStatusColorMimic) {
|
||||
if (CollUtil.isNotEmpty(chartStatusColor) && chartStatusColor.size() == 3) {
|
||||
for (int i = 0; i < chartStatusColor.size(); i++) {
|
||||
String statusColor = chartStatusColor.get(i);
|
||||
if (StrUtil.isNotEmpty(statusColor)) {
|
||||
String[] colorArr = statusColor.split(",");
|
||||
if (colorArr.length == 3) {
|
||||
ChartStatus.CUSTOM_COLOR.put(i, new Color(Integer.parseInt(colorArr[0]), Integer.parseInt(colorArr[1]), Integer.parseInt(colorArr[2])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (CollUtil.isNotEmpty(chartStatusColorClassics) && chartStatusColorClassics.size() == 3) {
|
||||
for (int i = 0; i < chartStatusColorClassics.size(); i++) {
|
||||
String statusColor = chartStatusColorClassics.get(i);
|
||||
if (StrUtil.isNotEmpty(statusColor)) {
|
||||
String[] colorArr = statusColor.split(",");
|
||||
if (colorArr.length == 3) {
|
||||
ChartStatus.CUSTOM_COLOR_CLASSICS.put(i, new Color(Integer.parseInt(colorArr[0]), Integer.parseInt(colorArr[1]), Integer.parseInt(colorArr[2])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (CollUtil.isNotEmpty(chartStatusColorMimic) && chartStatusColorMimic.size() == 3) {
|
||||
for (int i = 0; i < chartStatusColorMimic.size(); i++) {
|
||||
String statusColor = chartStatusColorMimic.get(i);
|
||||
if (StrUtil.isNotEmpty(statusColor)) {
|
||||
String[] colorArr = statusColor.split(",");
|
||||
if (colorArr.length == 3) {
|
||||
ChartStatus.CUSTOM_COLOR_MIMIC.put(i, new Color(Integer.parseInt(colorArr[0]), Integer.parseInt(colorArr[1]), Integer.parseInt(colorArr[2])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Color getNotDone(String modelValue) {
|
||||
return getColorByKey(ChartStatus.NOT_DONE, modelValue);
|
||||
}
|
||||
|
||||
public static Color getToDo(String modelValue) {
|
||||
return getColorByKey(ChartStatus.TO_DO, modelValue);
|
||||
}
|
||||
|
||||
public static Color getDone(String modelValue) {
|
||||
return getColorByKey(ChartStatus.DONE, modelValue);
|
||||
}
|
||||
|
||||
public static Color getColorByKey(ChartStatus chartStatus, String modelValue) {
|
||||
Color color = null;
|
||||
if (ModelEnum.CLASSICS.name().equals(modelValue)) {
|
||||
color = ChartStatus.CUSTOM_COLOR_CLASSICS.get(chartStatus.getKey());
|
||||
} else if (ModelEnum.MIMIC.name().equals(modelValue)) {
|
||||
color = ChartStatus.CUSTOM_COLOR_MIMIC.get(chartStatus.getKey());
|
||||
}
|
||||
if (ObjectUtil.isNull(color)) {
|
||||
color = ChartStatus.CUSTOM_COLOR.get(chartStatus.getKey());
|
||||
}
|
||||
return ObjectUtil.defaultIfNull(color, chartStatus.getColor());
|
||||
}
|
||||
|
||||
public static Color getColorByKey(Integer key) {
|
||||
for (ChartStatus item : ChartStatus.values()) {
|
||||
if (item.getKey().equals(key)) {
|
||||
Color color = ChartStatus.CUSTOM_COLOR.get(key);
|
||||
return ObjectUtil.defaultIfNull(color, item.getColor());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否未办理
|
||||
*
|
||||
* @param key 状态
|
||||
*/
|
||||
public static Boolean isNotDone(Integer key) {
|
||||
return ObjectUtil.isNotNull(key) && (ChartStatus.NOT_DONE.getKey().equals(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否待办理
|
||||
*
|
||||
* @param key 状态
|
||||
*/
|
||||
public static Boolean isToDo(Integer key) {
|
||||
return ObjectUtil.isNotNull(key) && (ChartStatus.TO_DO.getKey().equals(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否已办理
|
||||
*
|
||||
* @param key 状态
|
||||
*/
|
||||
public static Boolean isDone(Integer key) {
|
||||
return ObjectUtil.isNotNull(key) && (ChartStatus.DONE.getKey().equals(key));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import org.dromara.warm.flow.constant.ExceptionCons;
|
||||
import org.dromara.warm.flow.constant.FlowCons;
|
||||
import org.dromara.warm.flow.expression.SpelHelper;
|
||||
import org.dromara.warm.flow.strategy.ConditionStrategy;
|
||||
import org.dromara.warm.flow.utils.AssertUtil;
|
||||
import org.dromara.warm.flow.utils.MathUtil;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 内置条件表达式策略,合并原 eq/ne/gt/ge/lt/le/like/notLike/spel/default 十个策略类及 ConditionType 枚举
|
||||
* 表达式格式:eq@@flag|4(前缀 eq@@ 由 {@link ConditionStrategy} 截取,进入 eval 的为 flag|4)
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public enum Condition implements ConditionStrategy {
|
||||
|
||||
/**
|
||||
* 等于
|
||||
*/
|
||||
EQ("eq") {
|
||||
@Override
|
||||
public Boolean afterEval(String value, String variableValue) {
|
||||
if (MathUtil.isNumeric(value)) {
|
||||
return MathUtil.determineSize(variableValue, value) == 0;
|
||||
}
|
||||
return variableValue.equals(value);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 不等于
|
||||
*/
|
||||
NE("ne") {
|
||||
@Override
|
||||
public Boolean afterEval(String value, String variableValue) {
|
||||
if (MathUtil.isNumeric(value)) {
|
||||
return MathUtil.determineSize(variableValue, value) != 0;
|
||||
}
|
||||
return !variableValue.equals(value);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 大于(仅数值比较)
|
||||
*/
|
||||
GT("gt") {
|
||||
@Override
|
||||
public Boolean afterEval(String value, String variableValue) {
|
||||
return MathUtil.isNumeric(value) && MathUtil.determineSize(variableValue, value) > 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 大于等于(仅数值比较)
|
||||
*/
|
||||
GE("ge") {
|
||||
@Override
|
||||
public Boolean afterEval(String value, String variableValue) {
|
||||
return MathUtil.isNumeric(value) && MathUtil.determineSize(variableValue, value) >= 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 小于(仅数值比较)
|
||||
*/
|
||||
LT("lt") {
|
||||
@Override
|
||||
public Boolean afterEval(String value, String variableValue) {
|
||||
return MathUtil.isNumeric(value) && MathUtil.determineSize(variableValue, value) < 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 小于等于(仅数值比较)
|
||||
*/
|
||||
LE("le") {
|
||||
@Override
|
||||
public Boolean afterEval(String value, String variableValue) {
|
||||
return MathUtil.isNumeric(value) && MathUtil.determineSize(variableValue, value) <= 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 包含
|
||||
*/
|
||||
LIKE("like") {
|
||||
@Override
|
||||
public Boolean afterEval(String value, String variableValue) {
|
||||
return variableValue.contains(value);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 不包含
|
||||
*/
|
||||
NOT_LIKE("notLike") {
|
||||
@Override
|
||||
public Boolean afterEval(String value, String variableValue) {
|
||||
return !variableValue.contains(value);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* spel 条件表达式 spel@@#{@user.eval()}
|
||||
*/
|
||||
SPEL(FlowCons.SPEL) {
|
||||
@Override
|
||||
public Boolean eval(String expression, Map<String, Object> variable) {
|
||||
return SpelHelper.evalBool(expression, variable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean afterEval(String value, String variableValue) {
|
||||
throw new UnsupportedOperationException("spel 条件表达式不走值比较");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 默认条件表达式 default@@${flag == 5 && flag > 4},基于 spel 简化使用
|
||||
*/
|
||||
DEFAULT(FlowCons.DEFAULT) {
|
||||
@Override
|
||||
public Boolean eval(String expression, Map<String, Object> variable) {
|
||||
return SPEL.eval(SpelHelper.replace(expression, variable), variable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean afterEval(String value, String variableValue) {
|
||||
throw new UnsupportedOperationException("默认条件表达式不走值比较");
|
||||
}
|
||||
};
|
||||
|
||||
private final String key;
|
||||
|
||||
Condition(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean eval(String expression, Map<String, Object> variable) {
|
||||
AssertUtil.isEmpty(variable, ExceptionCons.NULL_CONDITION_VALUE);
|
||||
String[] split = expression.split(FlowCons.SPLIT_VERTICAL);
|
||||
String name = split[0].trim();
|
||||
Object o = variable.get(name);
|
||||
AssertUtil.isNull(o, ExceptionCons.NULL_CONDITION_VALUE);
|
||||
return afterEval(split[1].trim(), String.valueOf(o));
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较表达式值与流程变量值
|
||||
*
|
||||
* @param value 表达式最后一个参数,比如:eq@@flag|5 的 [5]
|
||||
* @param variableValue 流程变量值
|
||||
* @return 比较结果
|
||||
*/
|
||||
public abstract Boolean afterEval(String value, String variableValue);
|
||||
}
|
||||
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.dromara.warm.flow.constant.FlowCons;
|
||||
import org.dromara.warm.flow.utils.MathUtil;
|
||||
|
||||
|
||||
/**
|
||||
* 协作类型
|
||||
* APPROVAL-无:无其他协作方式
|
||||
* TRANSFER-转办:任务转给其他人办理
|
||||
* DEPUTE-委派:求助其他人审批,然后参照他的意见决定是否审批通过
|
||||
* COUNTERSIGN-会签:和其他人一起审批通过,才算通过
|
||||
* VOTE-票签:和部分人一起审批,达到一定通过率,才算通过
|
||||
* ADD_SIGNATURE-加签:办理中途,希望其他人一起参与办理
|
||||
* REDUCTION_SIGNATURE-减签:办理中途,希望某些人不参与办理
|
||||
*
|
||||
* @author xiarg
|
||||
* @since 2024/5/10 16:04
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum CooperateType {
|
||||
|
||||
/**
|
||||
* 协作类型
|
||||
*/
|
||||
APPROVAL(1, "无"),
|
||||
|
||||
TRANSFER(2, "转办"),
|
||||
|
||||
DEPUTE(3, "委派"),
|
||||
|
||||
COUNTERSIGN(4, "会签"),
|
||||
|
||||
VOTE(5, "票签"),
|
||||
|
||||
ADD_SIGNATURE(6, "加签"),
|
||||
|
||||
REDUCTION_SIGNATURE(7, "减签");
|
||||
|
||||
private final Integer key;
|
||||
private final String value;
|
||||
|
||||
|
||||
/**
|
||||
* 票签中的固定通过人数策略前缀
|
||||
*/
|
||||
public static final String PASS_COUNT = "passCount";
|
||||
/**
|
||||
* 票签中的固定驳回人数策略前缀
|
||||
*/
|
||||
public static final String REJECT_COUNT = "rejectCount";
|
||||
|
||||
/**
|
||||
* 顺签
|
||||
*/
|
||||
public static final String SEQUENCE = "sequence";
|
||||
|
||||
public static Integer getKeyByValue(String value) {
|
||||
for (CooperateType item : CooperateType.values()) {
|
||||
if (item.getValue().equals(value)) {
|
||||
return item.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getValueByKey(Integer key) {
|
||||
for (CooperateType item : CooperateType.values()) {
|
||||
if (item.getKey().equals(key)) {
|
||||
return item.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static CooperateType getByKey(Integer key) {
|
||||
for (CooperateType item : CooperateType.values()) {
|
||||
if (item.getKey().equals(key)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断是否为或签
|
||||
* @param ratio 比例
|
||||
* @return true:是;false:不是
|
||||
*/
|
||||
public static boolean isOrSign(String ratio) {
|
||||
return MathUtil.isZero(ratio);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是会签
|
||||
*
|
||||
* @param ratio 比例
|
||||
* @return true:是;false:不是
|
||||
*/
|
||||
public static boolean isCountersign(String ratio) {
|
||||
return MathUtil.isHundred(ratio);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是票签中通过率策略
|
||||
*
|
||||
* @param ratio 比例
|
||||
* @return true:是;false:不是
|
||||
*/
|
||||
public static boolean isVoteSignPassRatio(String ratio) {
|
||||
return MathUtil.isBetweenZeroAndHundred(ratio);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是票签中的固定通过人数策略
|
||||
*
|
||||
* @param passCount 固定通过人数
|
||||
* @return true:是;false:不是
|
||||
*/
|
||||
public static boolean isVoteSignPassCount(String passCount) {
|
||||
return StrUtil.isNotEmpty(passCount) && passCount.startsWith(PASS_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是票签中的固定驳回人数策略
|
||||
*
|
||||
* @param rejectCount 固定驳回人数
|
||||
* @return true:是;false:不是
|
||||
*/
|
||||
public static boolean isVoteSignRejectCount(String rejectCount) {
|
||||
return StrUtil.isNotEmpty(rejectCount) && rejectCount.startsWith(REJECT_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是票签中的默认表达式策略
|
||||
*
|
||||
* @param expression 默认表达式
|
||||
* @return true:是;false:不是
|
||||
*/
|
||||
public static boolean isVoteSignDefault(String expression) {
|
||||
return StrUtil.isNotEmpty(expression) && expression.startsWith(FlowCons.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是票签中的spel表达式策略
|
||||
*
|
||||
* @param expression spel表达式
|
||||
* @return true:是;false:不是
|
||||
*/
|
||||
public static boolean isVoteSignRejectSpel(String expression) {
|
||||
return StrUtil.isNotEmpty(expression) && expression.startsWith(FlowCons.SPEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是顺签
|
||||
*
|
||||
* @param expression 表达式
|
||||
* @return true:是;false:不是
|
||||
*/
|
||||
public static boolean isSequenceSign(String expression) {
|
||||
return StrUtil.isNotEmpty(expression) && expression.endsWith(FlowCons.SPLIT_AT + SEQUENCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是顺签
|
||||
*
|
||||
* @param expression 表达式
|
||||
* @return true:是;false:不是
|
||||
*/
|
||||
public static String removeSequence(String expression) {
|
||||
if (isSequenceSign(expression)) {
|
||||
return expression.substring(0, expression.lastIndexOf(FlowCons.SPLIT_AT + SEQUENCE));
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
/**
|
||||
* 流程状态
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023/3/31 12:16
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum FlowStatus {
|
||||
/**
|
||||
* 流程状态
|
||||
*/
|
||||
TOBESUBMIT("0", "待提交"),
|
||||
|
||||
APPROVAL("1", "审批中"),
|
||||
|
||||
PASS("2", "审批通过"),
|
||||
|
||||
AUTO_PASS("3", "自动完成"),
|
||||
|
||||
TERMINATE("4", "终止"),
|
||||
|
||||
NULLIFY("5", "作废"),
|
||||
|
||||
CANCEL("6", "撤销"),
|
||||
|
||||
RETRIEVE("7", "取回"),
|
||||
|
||||
FINISHED("8", "已完成"),
|
||||
|
||||
REJECT("9", "已退回"),
|
||||
|
||||
INVALID("10", "失效"),
|
||||
|
||||
TASK_BACK("11", "拿回"),
|
||||
|
||||
RE_START("12", "重启"),
|
||||
|
||||
PENDING("13", "暂存");
|
||||
|
||||
private final String key;
|
||||
private final String value;
|
||||
|
||||
public static String getKeyByValue(String value) {
|
||||
for (FlowStatus item : FlowStatus.values()) {
|
||||
if (item.getValue().equals(value)) {
|
||||
return item.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getValueByKey(String key) {
|
||||
for (FlowStatus item : FlowStatus.values()) {
|
||||
if (item.getKey().equals(key)) {
|
||||
return item.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static FlowStatus getByKey(String key) {
|
||||
for (FlowStatus item : FlowStatus.values()) {
|
||||
if (item.getKey().equals(key)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否结束节点
|
||||
*
|
||||
* @param key 枚举key
|
||||
* @return boolean
|
||||
*/
|
||||
public static Boolean isFinished(String key) {
|
||||
return ObjectUtil.isNotNull(key) && (FlowStatus.FINISHED.getKey().equals(key));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 表单类型
|
||||
*
|
||||
* @author warm
|
||||
* @since 2025/6/25
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum FormCustomEnum {
|
||||
|
||||
/**
|
||||
* 表单路径
|
||||
*/
|
||||
N,
|
||||
|
||||
/**
|
||||
* 表单路径
|
||||
*/
|
||||
Y,
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 框架类型: springboot、solon
|
||||
*
|
||||
* @author warm
|
||||
* @since 2026/3/24
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum FrameworkType {
|
||||
|
||||
SPRING_BOOT,
|
||||
|
||||
SOLON;
|
||||
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import org.dromara.warm.flow.expression.SpelHelper;
|
||||
import org.dromara.warm.flow.strategy.HandlerStrategy;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 内置办理人表达式策略,合并原 DefaultHandlerStrategy/HandlerStrategySpel 两个策略类
|
||||
* 表达式格式:${flag} 或 #{@user.evalVar()}
|
||||
*
|
||||
* @author warm,battcn
|
||||
*/
|
||||
public enum Handler implements HandlerStrategy {
|
||||
|
||||
/**
|
||||
* 默认办理人表达式 ${flag}
|
||||
*/
|
||||
DEFAULT("$") {
|
||||
@Override
|
||||
public Object preEval(String expression, Map<String, Object> variable) {
|
||||
String result = expression.replace("${", "").replace("}", "");
|
||||
return variable.get(result);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* spel 办理人表达式 #{@user.evalVar()}
|
||||
*/
|
||||
SPEL("#") {
|
||||
@Override
|
||||
public Object preEval(String expression, Map<String, Object> variable) {
|
||||
return SpelHelper.parseExpression(expression, variable);
|
||||
}
|
||||
};
|
||||
|
||||
private final String key;
|
||||
|
||||
Handler(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 设计器模型(CLASSICS经典模型 MIMIC仿钉钉模型)
|
||||
*
|
||||
* @author warm
|
||||
* @since 2025/6/25
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ModelEnum {
|
||||
|
||||
/**
|
||||
* 经典模型
|
||||
*/
|
||||
CLASSICS,
|
||||
|
||||
/**
|
||||
* 仿钉钉模型
|
||||
*/
|
||||
MIMIC
|
||||
}
|
||||
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
/**
|
||||
* 节点类型
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023/3/31 12:16
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum NodeType {
|
||||
/**
|
||||
* 开始节点
|
||||
*/
|
||||
START(0, "start"),
|
||||
/**
|
||||
* 中间节点
|
||||
*/
|
||||
BETWEEN(1, "between"),
|
||||
/**
|
||||
* 结束节点
|
||||
*/
|
||||
END(2, "end"),
|
||||
|
||||
/**
|
||||
* 互斥网关
|
||||
*/
|
||||
SERIAL(3, "serial"),
|
||||
|
||||
/**
|
||||
* 并行网关
|
||||
*/
|
||||
PARALLEL(4, "parallel"),
|
||||
|
||||
/**
|
||||
* 包容网关
|
||||
*/
|
||||
INCLUSIVE(5, "inclusive");
|
||||
|
||||
private final Integer key;
|
||||
private final String value;
|
||||
|
||||
public static Integer getKeyByValue(String value) {
|
||||
for (NodeType item : NodeType.values()) {
|
||||
if (item.getValue().equals(value)) {
|
||||
return item.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getValueByKey(Integer key) {
|
||||
for (NodeType item : NodeType.values()) {
|
||||
if (item.getKey().equals(key)) {
|
||||
return item.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static NodeType getByKey(Integer key) {
|
||||
for (NodeType item : NodeType.values()) {
|
||||
if (item.getKey().equals(key)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否开始节点
|
||||
*
|
||||
* @param key 枚举key
|
||||
* @return boolean
|
||||
*/
|
||||
public static Boolean isStart(Integer key) {
|
||||
return ObjectUtil.isNotNull(key) && (NodeType.START.getKey().equals(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否中间节点
|
||||
*
|
||||
* @param key 枚举key
|
||||
* @return boolean
|
||||
*/
|
||||
public static Boolean isBetween(Integer key) {
|
||||
return ObjectUtil.isNotNull(key) && (NodeType.BETWEEN.getKey().equals(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否结束节点
|
||||
*
|
||||
* @param key 枚举key
|
||||
* @return boolean
|
||||
*/
|
||||
public static Boolean isEnd(Integer key) {
|
||||
return ObjectUtil.isNotNull(key) && (NodeType.END.getKey().equals(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否网关节点
|
||||
*
|
||||
* @param key 枚举key
|
||||
* @return boolean
|
||||
*/
|
||||
public static Boolean isGateWay(Integer key) {
|
||||
return ObjectUtil.isNotNull(key) && (NodeType.SERIAL.getKey().equals(key)
|
||||
|| NodeType.PARALLEL.getKey().equals(key)|| NodeType.INCLUSIVE.getKey().equals(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否互斥网关节点
|
||||
*
|
||||
* @param key 枚举key
|
||||
* @return boolean
|
||||
*/
|
||||
public static Boolean isGateWaySerial(Integer key) {
|
||||
return ObjectUtil.isNotNull(key) && NodeType.SERIAL.getKey().equals(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否并行网关节点
|
||||
*
|
||||
* @param key 枚举key
|
||||
* @return boolean
|
||||
*/
|
||||
public static Boolean isGateWayParallel(Integer key) {
|
||||
return ObjectUtil.isNotNull(key) && NodeType.PARALLEL.getKey().equals(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否包容网关节点
|
||||
*
|
||||
* @param key 枚举key
|
||||
* @return boolean
|
||||
*/
|
||||
public static Boolean isGateWayInclusive(Integer key) {
|
||||
return ObjectUtil.isNotNull(key) && NodeType.INCLUSIVE.getKey().equals(key);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 节点类型
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023/3/31 12:16
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PublishStatus {
|
||||
|
||||
/**
|
||||
* 9=已失效;0=未发布;1=已发布
|
||||
*/
|
||||
EXPIRED(9, "已失效"),
|
||||
|
||||
UNPUBLISHED(0, "未发布"),
|
||||
|
||||
PUBLISHED(1, "已发布");
|
||||
|
||||
private final Integer key;
|
||||
private final String value;
|
||||
|
||||
public static Integer getKeyByValue(String value) {
|
||||
for (PublishStatus item : PublishStatus.values()) {
|
||||
if (item.getValue().equals(value)) {
|
||||
return item.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getValueByKey(Integer key) {
|
||||
for (PublishStatus item : PublishStatus.values()) {
|
||||
if (item.getKey().equals(key)) {
|
||||
return item.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static PublishStatus getByKey(Integer key) {
|
||||
for (PublishStatus item : PublishStatus.values()) {
|
||||
if (item.getKey().equals(key)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
/**
|
||||
* 审批动作
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023/3/31 12:16
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum SkipType {
|
||||
/**
|
||||
* 审批动作
|
||||
*/
|
||||
PASS("PASS", "审批通过"),
|
||||
|
||||
REJECT("REJECT", "退回"),
|
||||
|
||||
NONE("NONE", "无动作");
|
||||
|
||||
private final String key;
|
||||
private final String value;
|
||||
|
||||
public static String getKeyByValue(String value) {
|
||||
for (SkipType item : SkipType.values()) {
|
||||
if (item.getValue().equals(value)) {
|
||||
return item.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getValueByKey(String key) {
|
||||
for (SkipType item : SkipType.values()) {
|
||||
if (item.getKey().equals(key)) {
|
||||
return item.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static SkipType getByKey(String key) {
|
||||
for (SkipType item : SkipType.values()) {
|
||||
if (item.getKey().equals(key)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否通过类型
|
||||
*
|
||||
* @param key 枚举key
|
||||
* @return boolean
|
||||
*/
|
||||
public static Boolean isPass(String key) {
|
||||
return StrUtil.isNotEmpty(key) && (SkipType.PASS.getKey().equals(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否退回类型
|
||||
*
|
||||
* @param key 枚举key
|
||||
* @return boolean
|
||||
*/
|
||||
public static Boolean isReject(String key) {
|
||||
return StrUtil.isNotEmpty(key) && (SkipType.REJECT.getKey().equals(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否无动作类型
|
||||
*
|
||||
* @param key 枚举key
|
||||
* @return boolean
|
||||
*/
|
||||
public static Boolean isNone(String key) {
|
||||
return StrUtil.isNotEmpty(key) && (SkipType.NONE.getKey().equals(key));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 流程用户类型
|
||||
*
|
||||
* @author xiarg
|
||||
* @since 2024/5/10 16:04
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum UserType {
|
||||
|
||||
/**
|
||||
* 流程用户类型
|
||||
*/
|
||||
APPROVAL("1", "待办任务的审批人权限"),
|
||||
|
||||
TRANSFER("2", "待办任务的转办人权限"),
|
||||
|
||||
DEPUTE("3", "待办任务的委托人权限");
|
||||
|
||||
private final String key;
|
||||
private final String value;
|
||||
|
||||
public static String getKeyByValue(String value) {
|
||||
for (UserType item : UserType.values()) {
|
||||
if (item.getValue().equals(value)) {
|
||||
return item.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getValueByKey(String key) {
|
||||
for (UserType item : UserType.values()) {
|
||||
if (item.getKey().equals(key)) {
|
||||
return item.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static UserType getByKey(String key) {
|
||||
for (UserType item : UserType.values()) {
|
||||
if (item.getKey().equals(key)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.enums;
|
||||
|
||||
import org.dromara.warm.flow.constant.FlowCons;
|
||||
import org.dromara.warm.flow.expression.SpelHelper;
|
||||
import org.dromara.warm.flow.strategy.VoteSignStrategy;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 内置会签票签表达式策略,合并原 VoteSignStrategySpel/VoteSignStrategyDefault 两个策略类
|
||||
* 表达式格式:spel@@#{@user.eval()} 或 default@@${flag == 5 && flag > 4}
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public enum VoteSign implements VoteSignStrategy {
|
||||
|
||||
/**
|
||||
* spel 会签表达式 spel@@#{@user.eval()}
|
||||
*/
|
||||
SPEL(FlowCons.SPEL) {
|
||||
@Override
|
||||
public Boolean eval(String expression, Map<String, Object> variable) {
|
||||
return SpelHelper.evalBool(expression, variable);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 默认会签表达式 default@@${flag == 5 && flag > 4},基于 spel 简化使用
|
||||
*/
|
||||
DEFAULT(FlowCons.DEFAULT) {
|
||||
@Override
|
||||
public Boolean eval(String expression, Map<String, Object> variable) {
|
||||
return SPEL.eval(SpelHelper.replace(expression, variable), variable);
|
||||
}
|
||||
};
|
||||
|
||||
private final String key;
|
||||
|
||||
VoteSign(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 流程异常
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public final class FlowException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
@Getter
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 错误提示
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 错误明细,内部调试错误
|
||||
*/
|
||||
@Getter
|
||||
private String detailMessage;
|
||||
|
||||
/**
|
||||
* 空构造方法,避免反序列化问题
|
||||
*/
|
||||
public FlowException() {
|
||||
}
|
||||
|
||||
public FlowException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public FlowException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public FlowException(String message, Integer code) {
|
||||
this.message = message;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public FlowException setDetailMessage(String detailMessage) {
|
||||
this.detailMessage = detailMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public FlowException setMessage(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.expression;
|
||||
|
||||
import org.dromara.warm.flow.strategy.ListenerStrategy;
|
||||
import org.dromara.warm.flow.expression.SpelHelper;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* spel监听器表达式 #{@user.eval()}
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public class ListenerStrategySpel implements ListenerStrategy {
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "#";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean eval(String expression, Map<String, Object> variable) {
|
||||
SpelHelper.parseExpression(expression, variable);
|
||||
// 恒返回true,说明匹配上监听器表达式,扩展策略也一定要返回true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package org.dromara.warm.flow.expression;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.MethodExecutor;
|
||||
import org.springframework.expression.MethodResolver;
|
||||
import org.springframework.expression.spel.support.DataBindingMethodResolver;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 安全的方法解析器(限制可调用的方法)
|
||||
*
|
||||
* @author warm
|
||||
* @since 2026/3/31
|
||||
*/
|
||||
public class SafeMethodResolver implements MethodResolver {
|
||||
|
||||
private static final Set<String> DANGEROUS_METHODS = new HashSet<>(Arrays.asList(
|
||||
"getRuntime",
|
||||
"exec",
|
||||
"forName",
|
||||
"loadClass",
|
||||
"getClassLoader",
|
||||
"setAccessible",
|
||||
"newInstance",
|
||||
"invoke",
|
||||
"getField",
|
||||
"getDeclaredField",
|
||||
"getMethod",
|
||||
"getDeclaredMethod"
|
||||
));
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MethodExecutor resolve(@NonNull EvaluationContext context, @NonNull Object targetObject
|
||||
, @NonNull String name, @NonNull List<TypeDescriptor> argumentTypes) throws AccessException {
|
||||
if (DANGEROUS_METHODS.contains(name)) {
|
||||
throw new AccessException("不允许调用方法:" + name);
|
||||
}
|
||||
|
||||
// 委托给默认的方法解析器
|
||||
return DataBindingMethodResolver.forInstanceMethodInvocation()
|
||||
.resolve(context, targetObject, name, argumentTypes);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.expression;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import org.dromara.warm.flow.exception.FlowException;
|
||||
|
||||
import org.springframework.expression.TypeLocator;
|
||||
import org.springframework.expression.spel.support.StandardTypeLocator;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 自定义类型定位器,用于限制访问的类
|
||||
*
|
||||
* @author warm
|
||||
* @since 2026/3/31
|
||||
*/
|
||||
// ... existing code ...
|
||||
|
||||
/**
|
||||
* 自定义类型定位器,用于限制访问的类
|
||||
*
|
||||
* @author warm
|
||||
* @since 2026/3/31
|
||||
*/
|
||||
public class SafeTypeLocator implements TypeLocator {
|
||||
private final StandardTypeLocator defaultTypeLocator;
|
||||
private final Set<String> allowedClasses;
|
||||
private static final Set<String> DANGEROUS_CLASSES;
|
||||
|
||||
static {
|
||||
// 初始化危险类黑名单(使用全限定名便于前缀匹配)
|
||||
DANGEROUS_CLASSES = new HashSet<>();
|
||||
DANGEROUS_CLASSES.add("java.lang.Runtime");
|
||||
DANGEROUS_CLASSES.add("java.lang.ProcessBuilder");
|
||||
DANGEROUS_CLASSES.add("java.lang.System");
|
||||
DANGEROUS_CLASSES.add("java.lang.Class");
|
||||
DANGEROUS_CLASSES.add("java.lang.reflect");
|
||||
DANGEROUS_CLASSES.add("sun.misc.Unsafe");
|
||||
DANGEROUS_CLASSES.add("sun.reflect");
|
||||
}
|
||||
|
||||
{
|
||||
allowedClasses = new HashSet<>();
|
||||
allowedClasses.add("java.lang.String");
|
||||
allowedClasses.add("java.lang.Integer");
|
||||
allowedClasses.add("java.lang.Long");
|
||||
allowedClasses.add("java.lang.Double");
|
||||
allowedClasses.add("java.lang.Float");
|
||||
allowedClasses.add("java.lang.Boolean");
|
||||
allowedClasses.add("java.lang.Byte");
|
||||
allowedClasses.add("java.lang.Short");
|
||||
allowedClasses.add("java.lang.Character");
|
||||
allowedClasses.add("java.lang.Object");
|
||||
allowedClasses.add("java.util.List");
|
||||
allowedClasses.add("java.util.ArrayList");
|
||||
allowedClasses.add("java.util.Map");
|
||||
allowedClasses.add("java.util.HashMap");
|
||||
allowedClasses.add("java.util.Set");
|
||||
allowedClasses.add("java.util.HashSet");
|
||||
allowedClasses.add("java.util.Date");
|
||||
}
|
||||
|
||||
public SafeTypeLocator() {
|
||||
this.defaultTypeLocator = new StandardTypeLocator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> findType(@NonNull String typeName) {
|
||||
if (StrUtil.isEmpty(typeName)) {
|
||||
throw new FlowException("Type name cannot be null or empty");
|
||||
}
|
||||
|
||||
// 检查是否为危险类(黑名单)
|
||||
if (isDangerousClass(typeName)) {
|
||||
throw new FlowException("Forbidden dangerous class: " + typeName);
|
||||
}
|
||||
|
||||
// 检查是否在白名单内
|
||||
if (!allowedClasses.contains(typeName)) {
|
||||
throw new FlowException("Forbidden class not in whitelist: " + typeName);
|
||||
}
|
||||
|
||||
return defaultTypeLocator.findType(typeName);
|
||||
}
|
||||
|
||||
private boolean isDangerousClass(String typeName) {
|
||||
for (String dangerousClass : DANGEROUS_CLASSES) {
|
||||
if (typeName.equals(dangerousClass) || typeName.startsWith(dangerousClass + ".")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.expression;
|
||||
|
||||
import org.dromara.warm.flow.exception.FlowException;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.expression.BeanResolver;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.ParserContext;
|
||||
import org.springframework.expression.common.TemplateParserContext;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 条件表达式 spel
|
||||
*
|
||||
* @author warm,battcn
|
||||
*/
|
||||
@Configuration
|
||||
public class SpelHelper implements ApplicationContextAware {
|
||||
|
||||
private static final ExpressionParser PARSER = new SpelExpressionParser();
|
||||
private final static ParserContext PARSER_CONTEXT = new TemplateParserContext();
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* bean解析器 用于处理 spel 表达式中对 bean 的调用
|
||||
*/
|
||||
private static BeanResolver beanResolver = null;
|
||||
|
||||
public static BeanResolver beanResolver() {
|
||||
if (beanResolver == null) {
|
||||
beanResolver = new BeanFactoryResolver(getBeanFactory());
|
||||
}
|
||||
return beanResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param expression expression
|
||||
* @return Object
|
||||
*/
|
||||
public static Object parseExpression(String expression, Map<String, Object> variable) {
|
||||
|
||||
// 创建带沙箱保护的评估上下文
|
||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||
|
||||
// 设置 Bean 解析器(支持 @beanName 引用)
|
||||
context.setBeanResolver(beanResolver());
|
||||
|
||||
// 设置变量
|
||||
context.setVariables(variable);
|
||||
|
||||
// 设置沙箱:限制类型访问(白名单机制)
|
||||
context.setTypeLocator(new SafeTypeLocator());
|
||||
|
||||
// 设置沙箱:限制方法调用
|
||||
context.addMethodResolver(new SafeMethodResolver());
|
||||
|
||||
return PARSER.parseExpression(expression, PARSER_CONTEXT).getValue(context, Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 spel 表达式并按布尔结果返回
|
||||
*
|
||||
* @param expression 表达式
|
||||
* @param variable 流程变量
|
||||
* @return 布尔结果
|
||||
*/
|
||||
public static boolean evalBool(String expression, Map<String, Object> variable) {
|
||||
return Boolean.TRUE.equals(parseExpression(expression, variable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
|
||||
if (SpelHelper.applicationContext == null) {
|
||||
SpelHelper.applicationContext = applicationContext;
|
||||
}
|
||||
}
|
||||
|
||||
public static ListableBeanFactory getBeanFactory() {
|
||||
if (null == applicationContext) {
|
||||
throw new FlowException("No ConfigurableListableBeanFactory or ApplicationContext injected, maybe not in the Spring environment?");
|
||||
} else {
|
||||
return applicationContext;
|
||||
}
|
||||
}
|
||||
|
||||
public static String replace(String expression, Map<String, Object> variable) {
|
||||
expression = expression.replace("$", "#");
|
||||
for (Map.Entry<String, Object> entry : variable.entrySet()) {
|
||||
if (expression.contains(entry.getKey())) {
|
||||
expression = expression.replace(entry.getKey(), "#" + entry.getKey());
|
||||
}
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.handler;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.entity.RootEntity;
|
||||
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 数据填充handler,以下三个接口按照实际情况实现
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023/4/1 15:37
|
||||
*/
|
||||
public interface DataFillHandler {
|
||||
|
||||
Logger logger = LoggerFactory.getLogger(DataFillHandler.class);
|
||||
|
||||
/**
|
||||
* id填充。引擎在入库前就需要主键做行间互引(如历史任务的 instanceId/taskId),
|
||||
* 必须此处生成而非留给 MyBatis-Plus 在 insert 时补;与全局 IdentifierGenerator 同源,
|
||||
* insert 时对非空 id 不会重复生成
|
||||
*
|
||||
* @param object object
|
||||
*/
|
||||
default void idFill(Object object) {
|
||||
if (object instanceof RootEntity entity && ObjectUtil.isNull(entity.getId())) {
|
||||
IdentifierGenerator generator = SpringUtils.getBeanOrNull(IdentifierGenerator.class);
|
||||
entity.setId(generator != null ? generator.nextId(entity).longValue() : IdWorker.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增填充
|
||||
*
|
||||
* @param object object
|
||||
*/
|
||||
default void insertFill(Object object) {
|
||||
RootEntity entity = (RootEntity) object;
|
||||
if (ObjectUtil.isNull(entity)) {
|
||||
logger.warn("Insert operation failed - Reason: Entity is null after casting");
|
||||
return;
|
||||
}
|
||||
entity.setCreateTime(ObjectUtil.isNotNull(entity.getCreateTime()) ? entity.getCreateTime() : new Date());
|
||||
entity.setUpdateTime(ObjectUtil.isNotNull(entity.getUpdateTime()) ? entity.getUpdateTime() : new Date());
|
||||
|
||||
PermissionHandler permissionHandler = FlowEngine.permissionHandler();
|
||||
String handler = null;
|
||||
if (permissionHandler != null) {
|
||||
try {
|
||||
handler = permissionHandler.getHandler();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
entity.setCreateBy(StrUtil.isNotEmpty(handler) ? handler : entity.getCreateBy());
|
||||
entity.setUpdateBy(StrUtil.isNotEmpty(handler) ? handler : entity.getUpdateBy());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置更新常用参数
|
||||
*
|
||||
* @param object object
|
||||
*/
|
||||
default void updateFill(Object object) {
|
||||
RootEntity entity = (RootEntity) object;
|
||||
if (ObjectUtil.isNull(entity)) {
|
||||
logger.warn("Insert operation failed - Reason: Entity is null after casting");
|
||||
return;
|
||||
}
|
||||
entity.setUpdateTime(ObjectUtil.isNotNull(entity.getUpdateTime()) ? entity.getUpdateTime() : new Date());
|
||||
PermissionHandler permissionHandler = FlowEngine.permissionHandler();
|
||||
String handler = null;
|
||||
if (permissionHandler != null) {
|
||||
try {
|
||||
handler = permissionHandler.getHandler();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
entity.setUpdateBy(StrUtil.isNotEmpty(handler) ? handler : entity.getUpdateBy());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.handler;
|
||||
|
||||
import org.dromara.warm.flow.dto.FlowParams;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 办理人权限处理器
|
||||
* 用户获取工作流中用到的permissionFlag和handler
|
||||
* permissionFlag: 办理人权限标识,比如用户,角色,部门等,用于校验是否有权限办理任务
|
||||
* handler: 当前办理人唯一标识,就是确定唯一用的,如用户id,通常用来入库,记录流程实例创建人,办理人
|
||||
*
|
||||
* @author shadow
|
||||
*/
|
||||
public interface PermissionHandler {
|
||||
|
||||
/**
|
||||
* 办理人权限标识,比如用户,角色,部门等,用于校验是否有权限办理任务
|
||||
* 后续在{@link FlowParams#getPermissionFlag} 中获取
|
||||
* 返回当前用户权限集合
|
||||
*
|
||||
*/
|
||||
List<String> permissions();
|
||||
|
||||
/**
|
||||
* 获取当前办理人:就是确定唯一用的,如用户id,通常用来入库,记录流程实例创建人,办理人
|
||||
* 后续在{@link FlowParams#getHandler()} 中获取
|
||||
*
|
||||
* @return 当前办理人
|
||||
*/
|
||||
String getHandler();
|
||||
|
||||
/**
|
||||
* 转换办理人,比如设计器中预设了能办理的人,如果其中包含角色或者部门id等,可以通过此接口进行转换成用户id
|
||||
*/
|
||||
default List<String> convertPermissions(List<String> permissions) {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.json;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.dromara.warm.flow.exception.FlowException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.DeserializationFeature;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Jackson 3:map和json字符串转换工具类
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public final class JsonUtil {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(JsonUtil.class);
|
||||
|
||||
private static final JsonMapper JSON_MAPPER = JsonMapper.builder()
|
||||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||
.build();
|
||||
|
||||
private JsonUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转为map
|
||||
*
|
||||
* @param jsonStr json字符串
|
||||
* @return map
|
||||
*/
|
||||
public static Map<String, Object> strToMap(String jsonStr) {
|
||||
if (StrUtil.isNotEmpty(jsonStr)) {
|
||||
try {
|
||||
return JSON_MAPPER.readValue(jsonStr,
|
||||
JSON_MAPPER.getTypeFactory().constructMapType(Map.class, String.class, Object.class));
|
||||
} catch (Exception e) {
|
||||
log.error("json转换异常", e);
|
||||
throw new FlowException("json转换异常");
|
||||
}
|
||||
}
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转为bean
|
||||
*
|
||||
* @param jsonStr json字符串
|
||||
* @param clazz Class<T>
|
||||
* @return T
|
||||
*/
|
||||
public static <T> T strToBean(String jsonStr, Class<T> clazz) {
|
||||
if (StrUtil.isNotEmpty(jsonStr)) {
|
||||
try {
|
||||
return JSON_MAPPER.readValue(jsonStr, clazz);
|
||||
} catch (Exception e) {
|
||||
log.error("json转换异常", e);
|
||||
throw new FlowException("json转换异常");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转为集合
|
||||
*
|
||||
* @param jsonStr json字符串
|
||||
* @return List<T>
|
||||
*/
|
||||
public static <T> List<T> strToList(String jsonStr) {
|
||||
if (StrUtil.isNotEmpty(jsonStr)) {
|
||||
try {
|
||||
return JSON_MAPPER.readValue(jsonStr, new TypeReference<List<T>>() {
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error("json转换异常", e);
|
||||
throw new FlowException("json转换异常");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转为字符串
|
||||
*
|
||||
* @param variable object
|
||||
* @return json字符串
|
||||
*/
|
||||
public static String objToStr(Object variable) {
|
||||
if (ObjectUtil.isNotNull(variable)) {
|
||||
try {
|
||||
return JSON_MAPPER.writeValueAsString(variable);
|
||||
} catch (Exception e) {
|
||||
log.error("Map转换异常", e);
|
||||
throw new FlowException("Map转换异常");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.listener;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 全局监听器: 整个系统只有一个,任务开始、分派、完成和创建、时期执行
|
||||
*
|
||||
* @author warm
|
||||
* @since 2024/11/17
|
||||
*/
|
||||
public interface GlobalListener extends Serializable {
|
||||
|
||||
/**
|
||||
* 开始监听器,任务开始办理时执行
|
||||
*
|
||||
* @param listenerVariable 监听器变量
|
||||
*/
|
||||
default void start(ListenerVariable listenerVariable) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 分派监听器,动态修改代办任务信息
|
||||
*
|
||||
* @param listenerVariable 监听器变量
|
||||
*/
|
||||
default void assignment(ListenerVariable listenerVariable) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成监听器,当前任务完成后执行
|
||||
*
|
||||
* @param listenerVariable 监听器变量
|
||||
*/
|
||||
default void finish(ListenerVariable listenerVariable) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建监听器,任务创建时执行
|
||||
*
|
||||
* @param listenerVariable 监听器变量
|
||||
*/
|
||||
default void create(ListenerVariable listenerVariable) {
|
||||
|
||||
}
|
||||
|
||||
default void notify(String type, ListenerVariable listenerVariable) {
|
||||
switch (type) {
|
||||
case Listener.LISTENER_START:
|
||||
start(listenerVariable);
|
||||
break;
|
||||
case Listener.LISTENER_ASSIGNMENT:
|
||||
assignment(listenerVariable);
|
||||
break;
|
||||
case Listener.LISTENER_FINISH:
|
||||
finish(listenerVariable);
|
||||
break;
|
||||
case Listener.LISTENER_CREATE:
|
||||
create(listenerVariable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.listener;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 监听器
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public interface Listener extends Serializable {
|
||||
|
||||
/**
|
||||
* 开始监听器,任务开始办理时执行
|
||||
*/
|
||||
String LISTENER_START = "start";
|
||||
|
||||
/**
|
||||
* 分派监听器,动态修改代办任务信息
|
||||
*/
|
||||
String LISTENER_ASSIGNMENT = "assignment";
|
||||
|
||||
/**
|
||||
* 完成监听器,当前任务完成后执行
|
||||
*/
|
||||
String LISTENER_FINISH = "finish";
|
||||
|
||||
/**
|
||||
* 创建监听器,任务创建时执行
|
||||
*/
|
||||
String LISTENER_CREATE = "create";
|
||||
|
||||
/**
|
||||
* 表单数据加载监听器,1.3.0 内置表单使用
|
||||
*/
|
||||
String LISTENER_FORM_LOAD = "formLoad";
|
||||
|
||||
/**
|
||||
* 通知
|
||||
*
|
||||
* @param variable variable
|
||||
*/
|
||||
void notify(ListenerVariable variable);
|
||||
}
|
||||
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.listener;
|
||||
|
||||
import org.dromara.warm.flow.dto.FlowParams;
|
||||
import org.dromara.warm.flow.entity.FlowDefinition;
|
||||
import org.dromara.warm.flow.entity.FlowInstance;
|
||||
import org.dromara.warm.flow.entity.FlowNode;
|
||||
import org.dromara.warm.flow.entity.FlowTask;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 监听器变量
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public class ListenerVariable {
|
||||
|
||||
/**
|
||||
* 流程定义
|
||||
*/
|
||||
private FlowDefinition definition;
|
||||
|
||||
/**
|
||||
* 流程实例
|
||||
*/
|
||||
private FlowInstance instance;
|
||||
|
||||
/**
|
||||
* 监听器对应的节点
|
||||
*/
|
||||
private FlowNode node;
|
||||
|
||||
/**
|
||||
* 当前任务
|
||||
*/
|
||||
private FlowTask task;
|
||||
|
||||
/**
|
||||
* 下一次执行的节点集合
|
||||
*/
|
||||
private List<FlowNode> nextNodes;
|
||||
|
||||
/**
|
||||
* 新创建任务集合
|
||||
*/
|
||||
private List<FlowTask> nextTasks;
|
||||
|
||||
/**
|
||||
* 流程变量
|
||||
*/
|
||||
private Map<String, Object> variable;
|
||||
|
||||
/**
|
||||
* 工作流内置参数
|
||||
*/
|
||||
private FlowParams flowParams;
|
||||
|
||||
|
||||
public ListenerVariable() {
|
||||
}
|
||||
|
||||
public ListenerVariable(FlowDefinition definition, FlowInstance instance, Map<String, Object> variable) {
|
||||
this.definition = definition;
|
||||
this.instance = instance;
|
||||
this.variable = variable;
|
||||
}
|
||||
|
||||
public ListenerVariable(FlowDefinition definition, FlowInstance instance, FlowNode node, Map<String, Object> variable) {
|
||||
this.definition = definition;
|
||||
this.instance = instance;
|
||||
this.node = node;
|
||||
this.variable = variable;
|
||||
}
|
||||
|
||||
public ListenerVariable(FlowDefinition definition, FlowInstance instance, Map<String, Object> variable, FlowTask task) {
|
||||
this.definition = definition;
|
||||
this.instance = instance;
|
||||
this.variable = variable;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public ListenerVariable(FlowDefinition definition, FlowInstance instance, FlowNode node, Map<String, Object> variable, FlowTask task) {
|
||||
this.definition = definition;
|
||||
this.instance = instance;
|
||||
this.node = node;
|
||||
this.variable = variable;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public ListenerVariable(FlowDefinition definition, FlowInstance instance, FlowNode node, Map<String, Object> variable, FlowTask task, List<FlowNode> nextNodes) {
|
||||
this.definition = definition;
|
||||
this.instance = instance;
|
||||
this.node = node;
|
||||
this.variable = variable;
|
||||
this.task = task;
|
||||
this.nextNodes = nextNodes;
|
||||
}
|
||||
|
||||
public ListenerVariable(FlowDefinition definition, FlowInstance instance, FlowNode node, Map<String, Object> variable, FlowTask task
|
||||
, List<FlowNode> nextNodes, List<FlowTask> nextTasks) {
|
||||
this.definition = definition;
|
||||
this.instance = instance;
|
||||
this.node = node;
|
||||
this.variable = variable;
|
||||
this.task = task;
|
||||
this.nextNodes = nextNodes;
|
||||
this.nextTasks = nextTasks;
|
||||
}
|
||||
|
||||
public FlowDefinition getDefinition() {
|
||||
return definition;
|
||||
}
|
||||
|
||||
public ListenerVariable setDefinition(FlowDefinition definition) {
|
||||
this.definition = definition;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowInstance getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public ListenerVariable setInstance(FlowInstance instance) {
|
||||
this.instance = instance;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowNode getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public ListenerVariable setNode(FlowNode node) {
|
||||
this.node = node;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowTask getTask() {
|
||||
return task;
|
||||
}
|
||||
|
||||
public ListenerVariable setTask(FlowTask task) {
|
||||
this.task = task;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<FlowNode> getNextNodes() {
|
||||
return nextNodes;
|
||||
}
|
||||
|
||||
public ListenerVariable setNextNodes(List<FlowNode> nextNodes) {
|
||||
this.nextNodes = nextNodes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<FlowTask> getNextTasks() {
|
||||
return nextTasks;
|
||||
}
|
||||
|
||||
public ListenerVariable setNextTasks(List<FlowTask> nextTasks) {
|
||||
this.nextTasks = nextTasks;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, Object> getVariable() {
|
||||
return variable;
|
||||
}
|
||||
|
||||
public ListenerVariable setVariable(Map<String, Object> variable) {
|
||||
this.variable = variable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FlowParams getFlowParams() {
|
||||
return flowParams;
|
||||
}
|
||||
|
||||
public ListenerVariable setFlowParams(FlowParams flowParams) {
|
||||
this.flowParams = flowParams;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ListenerVariable{" +
|
||||
"definition=" + definition +
|
||||
", instance=" + instance +
|
||||
", node=" + node +
|
||||
", task=" + task +
|
||||
", nextNodes=" + nextNodes +
|
||||
", nextTasks=" + nextTasks +
|
||||
", variable=" + variable +
|
||||
", flowParams=" + flowParams +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.listener;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author warm
|
||||
*/
|
||||
@Data
|
||||
public class ValueHolder {
|
||||
|
||||
/**
|
||||
* 路径
|
||||
*/
|
||||
private String path;
|
||||
/**
|
||||
* 监听器
|
||||
*/
|
||||
private Listener listener;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private String params;
|
||||
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.dromara.warm.flow.entity.FlowDefinition;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程定义Mapper接口
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
public interface FlowDefinitionMapper extends WarmMapper<FlowDefinition> {
|
||||
|
||||
/**
|
||||
* 根据编码批量查询
|
||||
*
|
||||
* @param flowCodeList 流程编码集
|
||||
* @return 查询结果
|
||||
*/
|
||||
default List<FlowDefinition> queryByCodeList(List<String> flowCodeList) {
|
||||
LambdaQueryWrapper<FlowDefinition> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(FlowDefinition::getFlowCode, flowCodeList);
|
||||
return selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID批量修改发布状态
|
||||
*
|
||||
* @param ids ids
|
||||
* @param publishStatus 发布状态(9=已失效;0=未发布;1=已发布)
|
||||
* @see org.dromara.warm.flow.enums.PublishStatus
|
||||
*/
|
||||
default void updatePublishStatus(List<Long> ids, Integer publishStatus) {
|
||||
LambdaQueryWrapper<FlowDefinition> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(FlowDefinition::getId, ids);
|
||||
update(new FlowDefinition().setIsPublish(publishStatus), queryWrapper);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.mapper;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.dromara.warm.flow.entity.FlowHisTask;
|
||||
import org.dromara.warm.flow.enums.SkipType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 历史任务记录Mapper接口
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
public interface FlowHisTaskMapper extends WarmMapper<FlowHisTask> {
|
||||
|
||||
/**
|
||||
* 根据instanceId获取未退回的历史记录
|
||||
*/
|
||||
default List<FlowHisTask> getNoReject(Long instanceId) {
|
||||
LambdaQueryWrapper<FlowHisTask> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FlowHisTask::getInstanceId, instanceId)
|
||||
.eq(FlowHisTask::getSkipType, SkipType.PASS.getKey())
|
||||
.orderByDesc(FlowHisTask::getCreateTime);
|
||||
return selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据instanceId和流程编码获取未退回的历史记录
|
||||
*/
|
||||
default List<FlowHisTask> getByInsAndNodeCodes(Long instanceId, List<String> nodeCodes) {
|
||||
LambdaQueryWrapper<FlowHisTask> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FlowHisTask::getInstanceId, instanceId)
|
||||
.in(CollUtil.isNotEmpty(nodeCodes), FlowHisTask::getNodeCode, nodeCodes)
|
||||
.orderByDesc(FlowHisTask::getCreateTime);
|
||||
return selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据instanceIds删除
|
||||
*
|
||||
* @param instanceIds 主键
|
||||
* @return 结果
|
||||
*/
|
||||
default int deleteByInsIds(List<Long> instanceIds) {
|
||||
return delete(new LambdaQueryWrapper<FlowHisTask>().in(FlowHisTask::getInstanceId, instanceIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务id和协作类型查询
|
||||
*/
|
||||
default List<FlowHisTask> listByTaskIdAndCooperateTypes(Long taskId, Integer[] cooperateTypes) {
|
||||
LambdaQueryWrapper<FlowHisTask> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FlowHisTask::getTaskId, taskId).in(FlowHisTask::getCooperateType, Arrays.asList(cooperateTypes));
|
||||
return selectList(queryWrapper);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.dromara.warm.flow.entity.FlowInstance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程实例Mapper接口
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
public interface FlowInstanceMapper extends WarmMapper<FlowInstance> {
|
||||
|
||||
/**
|
||||
* 根据流程定义ID,查询流程实例集合
|
||||
*
|
||||
* @param defIds 流程定义ID集合
|
||||
* @return 流程实例集合
|
||||
*/
|
||||
default List<FlowInstance> getByDefIds(List<Long> defIds) {
|
||||
LambdaQueryWrapper<FlowInstance> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(FlowInstance::getDefinitionId, defIds);
|
||||
return selectList(queryWrapper);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.mapper;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.dromara.warm.flow.entity.FlowNode;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程节点Mapper接口
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
public interface FlowNodeMapper extends WarmMapper<FlowNode> {
|
||||
|
||||
default List<FlowNode> getByNodeCodes(List<String> nodeCodes, Long definitionId) {
|
||||
LambdaQueryWrapper<FlowNode> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(CollUtil.isNotEmpty(nodeCodes), FlowNode::getNodeCode, nodeCodes)
|
||||
.eq(FlowNode::getDefinitionId, definitionId);
|
||||
return selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除流程节点
|
||||
*
|
||||
* @param defIds 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
default int deleteNodeByDefIds(Collection<? extends Serializable> defIds) {
|
||||
return delete(new LambdaQueryWrapper<FlowNode>().in(FlowNode::getDefinitionId, defIds));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.dromara.warm.flow.entity.FlowSkip;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 节点跳转关联Mapper接口
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
public interface FlowSkipMapper extends WarmMapper<FlowSkip> {
|
||||
|
||||
/**
|
||||
* 批量删除节点跳转关联
|
||||
*
|
||||
* @param defIds 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
default int deleteSkipByDefIds(Collection<? extends Serializable> defIds) {
|
||||
return delete(new LambdaQueryWrapper<FlowSkip>().in(FlowSkip::getDefinitionId, defIds));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.dromara.warm.flow.entity.FlowTask;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 待办任务Mapper接口
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
public interface FlowTaskMapper extends WarmMapper<FlowTask> {
|
||||
|
||||
/**
|
||||
* 根据instanceIds删除
|
||||
*
|
||||
* @param instanceIds 主键
|
||||
* @return 结果
|
||||
*/
|
||||
default int deleteByInsIds(List<Long> instanceIds) {
|
||||
return delete(new LambdaQueryWrapper<FlowTask>().in(FlowTask::getInstanceId, instanceIds));
|
||||
}
|
||||
|
||||
default List<FlowTask> getByInsIdAndNodeCodes(Long instanceId, List<String> nodeCodes) {
|
||||
LambdaQueryWrapper<FlowTask> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FlowTask::getInstanceId, instanceId);
|
||||
queryWrapper.in(FlowTask::getNodeCode, nodeCodes);
|
||||
return selectList(queryWrapper);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.mapper;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.dromara.warm.flow.entity.FlowUser;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程用户Mapper接口
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
public interface FlowUserMapper extends WarmMapper<FlowUser> {
|
||||
|
||||
/**
|
||||
* 根据taskId删除
|
||||
*
|
||||
* @param taskIdList 待办任务主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
default int deleteByTaskIds(List<Long> taskIdList) {
|
||||
return delete(new LambdaQueryWrapper<FlowUser>().in(FlowUser::getAssociated, taskIdList));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据(待办任务,实例,历史表,节点等)id查询权限人或者处理人
|
||||
*
|
||||
* @param associatedList (待办任务,实例,历史表,节点等)id集合
|
||||
* @param types 用户表类型
|
||||
* @return 查询结果
|
||||
*/
|
||||
default List<FlowUser> listByAssociatedAndTypes(List<Long> associatedList, String[] types) {
|
||||
LambdaQueryWrapper<FlowUser> queryWrapper = new LambdaQueryWrapper<>();
|
||||
if (CollUtil.isNotEmpty(associatedList)) {
|
||||
if (associatedList.size() == 1) {
|
||||
queryWrapper.eq(FlowUser::getAssociated, associatedList.get(0));
|
||||
} else {
|
||||
queryWrapper.in(FlowUser::getAssociated, associatedList);
|
||||
}
|
||||
}
|
||||
queryWrapper.in(ArrayUtil.isNotEmpty(types), FlowUser::getType, Arrays.asList(types));
|
||||
return selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据办理人查询
|
||||
*
|
||||
* @param associated 待办任务id
|
||||
* @param processedBys 办理人id集合
|
||||
* @param types 用户表类型
|
||||
* @return 查询结果
|
||||
*/
|
||||
default List<FlowUser> listByProcessedBys(Long associated, List<String> processedBys, String[] types) {
|
||||
LambdaQueryWrapper<FlowUser> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(ObjectUtil.isNotNull(associated), FlowUser::getAssociated, associated);
|
||||
if (CollUtil.isNotEmpty(processedBys)) {
|
||||
if (processedBys.size() == 1) {
|
||||
queryWrapper.eq(FlowUser::getProcessedBy, processedBys.get(0));
|
||||
} else {
|
||||
queryWrapper.in(FlowUser::getProcessedBy, processedBys);
|
||||
}
|
||||
}
|
||||
queryWrapper.in(ArrayUtil.isNotEmpty(types), FlowUser::getType, types);
|
||||
return selectList(queryWrapper);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.dromara.warm.flow.entity.RootEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BaseMapper接口(含原 Dao 层通用查询,default 方法由 MyBatis 直接调用)
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-17
|
||||
*/
|
||||
public interface WarmMapper<T extends RootEntity> extends BaseMapper<T> {
|
||||
|
||||
/**
|
||||
* 实体条件查询
|
||||
*
|
||||
* @param entity 条件实体
|
||||
*/
|
||||
default List<T> selectList(T entity) {
|
||||
return selectList(new QueryWrapper<>(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体条件计数(接口类型 lambda 需显式设置实体类)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default long selectCount(T entity) {
|
||||
LambdaQueryWrapper<T> queryWrapper = new LambdaQueryWrapper<>(entity);
|
||||
queryWrapper.setEntityClass((Class<T>) entity.getClass());
|
||||
return selectCount(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按主键更新(接口类型 lambda 需显式设置实体类)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default int update(T entity) {
|
||||
LambdaQueryWrapper<T> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.setEntityClass((Class<T>) entity.getClass());
|
||||
queryWrapper.eq(RootEntity::getId, entity.getId());
|
||||
return update(entity, queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体条件删除
|
||||
*/
|
||||
default int delete(T entity) {
|
||||
return delete(new LambdaQueryWrapper<>(entity));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.service;
|
||||
|
||||
import org.dromara.warm.flow.json.JsonUtil;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.dto.DefJson;
|
||||
import org.dromara.warm.flow.dto.NodeJson;
|
||||
import org.dromara.warm.flow.dto.PathWayData;
|
||||
import org.dromara.warm.flow.dto.SkipJson;
|
||||
import org.dromara.warm.flow.entity.FlowInstance;
|
||||
import org.dromara.warm.flow.entity.FlowSkip;
|
||||
import org.dromara.warm.flow.enums.ChartStatus;
|
||||
import org.dromara.warm.flow.enums.NodeType;
|
||||
import org.dromara.warm.flow.enums.SkipType;
|
||||
|
||||
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 流程图绘制Service业务层处理
|
||||
*
|
||||
* @author warm
|
||||
* @since 2024-12-30
|
||||
*/
|
||||
@Service
|
||||
public class ChartService {
|
||||
|
||||
public String startMetadata(PathWayData pathWayData) {
|
||||
|
||||
DefJson defJson = FlowEngine.defService().queryDesign(pathWayData.getDefId());
|
||||
List<NodeJson> nodeList = defJson.getNodeList();
|
||||
|
||||
Map<String, NodeJson> nodeMap = StreamUtils.toMap(nodeList, NodeJson::getNodeCode
|
||||
, node -> node.setStatus(ChartStatus.NOT_DONE.getKey()));
|
||||
Map<String, SkipJson> skipMap = nodeList.stream().map(NodeJson::getSkipList).flatMap(List::stream)
|
||||
.collect(Collectors.toMap(this::getSkipKey, skip -> skip.setStatus(ChartStatus.NOT_DONE.getKey())));
|
||||
|
||||
pathWayData.getPathWayNodes().forEach(node -> nodeMap.get(node.getNodeCode()).setStatus(ChartStatus.DONE.getKey()));
|
||||
pathWayData.getPathWaySkips().forEach(skip -> skipMap.get(getSkipKey(skip)).setStatus(ChartStatus.DONE.getKey()));
|
||||
pathWayData.getTargetNodes().forEach(node -> nodeMap.get(node.getNodeCode()).setStatus(
|
||||
NodeType.isEnd(node.getNodeType()) ? ChartStatus.DONE.getKey() : ChartStatus.TO_DO.getKey()
|
||||
));
|
||||
|
||||
return JsonUtil.objToStr(defJson);
|
||||
}
|
||||
|
||||
public String skipMetadata(PathWayData pathWayData) {
|
||||
FlowInstance instance = FlowEngine.insService().getById(pathWayData.getInsId());
|
||||
DefJson defJson = JsonUtil.strToBean(instance.getDefJson(), DefJson.class);
|
||||
|
||||
List<NodeJson> nodeList = defJson.getNodeList();
|
||||
List<SkipJson> skipList = defJson.getNodeList().stream().map(NodeJson::getSkipList)
|
||||
.filter(Objects::nonNull).flatMap(List::stream).collect(Collectors.toList());
|
||||
Map<String, NodeJson> nodeMap = StreamUtils.toMap(nodeList, NodeJson::getNodeCode, node -> node);
|
||||
Map<String, SkipJson> skipMap = StreamUtils.toMap(skipList, this::getSkipKey, skip -> skip);
|
||||
|
||||
pathWayData.getPathWayNodes().forEach(node -> {
|
||||
NodeJson nodeJson = nodeMap.get(node.getNodeCode());
|
||||
if (SkipType.isPass(pathWayData.getSkipType())) {
|
||||
nodeJson.setStatus(ChartStatus.DONE.getKey());
|
||||
} else if (SkipType.isReject(pathWayData.getSkipType())) {
|
||||
nodeJson.setStatus(ChartStatus.NOT_DONE.getKey());
|
||||
}
|
||||
});
|
||||
pathWayData.getPathWaySkips().forEach(skip -> {
|
||||
SkipJson skipJson = skipMap.get(getSkipKey(skip));
|
||||
if (SkipType.isPass(pathWayData.getSkipType())) {
|
||||
skipJson.setStatus(ChartStatus.DONE.getKey());
|
||||
} else if (SkipType.isReject(pathWayData.getSkipType())) {
|
||||
skipJson.setStatus(ChartStatus.NOT_DONE.getKey());
|
||||
}
|
||||
});
|
||||
pathWayData.getTargetNodes().forEach(node -> {
|
||||
NodeJson nodeJson = nodeMap.get(node.getNodeCode());
|
||||
if (NodeType.isEnd(node.getNodeType())) {
|
||||
nodeJson.setStatus(ChartStatus.DONE.getKey());
|
||||
} else {
|
||||
nodeJson.setStatus(ChartStatus.TO_DO.getKey());
|
||||
}
|
||||
});
|
||||
|
||||
if (SkipType.isReject(pathWayData.getSkipType())) {
|
||||
Map<String, List<SkipJson>> skipNextMap = StreamUtils.groupByKey(
|
||||
StreamUtils.filter(skipList, skip -> !SkipType.isReject(skip.getSkipType())), SkipJson::getNowNodeCode);
|
||||
pathWayData.getTargetNodes().forEach(node -> rejectReset(node.getNodeCode(), skipNextMap, nodeMap));
|
||||
}
|
||||
|
||||
pathWayData.getTargetNodes().forEach(node -> {
|
||||
if (NodeType.isEnd(node.getNodeType())) {
|
||||
nodeList.forEach(nodeJson -> {
|
||||
if (ChartStatus.isToDo(nodeJson.getStatus())) {
|
||||
nodeJson.setStatus(ChartStatus.NOT_DONE.getKey());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return JsonUtil.objToStr(defJson);
|
||||
}
|
||||
|
||||
public List<String> getChartRgb(String modelValue) {
|
||||
List<String> chartStatusColor = new ArrayList<>();
|
||||
Color done = ChartStatus.getDone(modelValue);
|
||||
chartStatusColor.add(done.getRed() + "," + done.getGreen() + "," + done.getBlue());
|
||||
Color toDo = ChartStatus.getToDo(modelValue);
|
||||
chartStatusColor.add(toDo.getRed() + "," + toDo.getGreen() + "," + toDo.getBlue());
|
||||
Color notDone = ChartStatus.getNotDone(modelValue);
|
||||
chartStatusColor.add(notDone.getRed() + "," + notDone.getGreen() + "," + notDone.getBlue());
|
||||
return chartStatusColor;
|
||||
}
|
||||
|
||||
private String getSkipKey(SkipJson skip) {
|
||||
return ArrayUtil.join(new String[]{
|
||||
skip.getNowNodeCode(),
|
||||
skip.getSkipType(),
|
||||
skip.getSkipCondition(),
|
||||
skip.getNextNodeCode()}, ":");
|
||||
}
|
||||
|
||||
private String getSkipKey(FlowSkip skip) {
|
||||
return ArrayUtil.join(new String[]{
|
||||
skip.getNowNodeCode(),
|
||||
skip.getSkipType(),
|
||||
skip.getSkipCondition(),
|
||||
skip.getNextNodeCode()}, ":");
|
||||
}
|
||||
|
||||
private void rejectReset(String nodeCode, Map<String, List<SkipJson>> skipNextMap, Map<String, NodeJson> nodeMap) {
|
||||
List<SkipJson> oneNextSkips = skipNextMap.get(nodeCode);
|
||||
if (CollUtil.isNotEmpty(oneNextSkips)) {
|
||||
oneNextSkips.forEach(oneNextSkip -> {
|
||||
if (ObjectUtil.isNotNull(oneNextSkip) && !ChartStatus.isNotDone(oneNextSkip.getStatus())) {
|
||||
oneNextSkip.setStatus(ChartStatus.NOT_DONE.getKey());
|
||||
NodeJson nodeJson = nodeMap.get(oneNextSkip.getNextNodeCode());
|
||||
if (ObjectUtil.isNotNull(nodeJson) && !ChartStatus.isNotDone(nodeJson.getStatus())) {
|
||||
nodeJson.setStatus(ChartStatus.NOT_DONE.getKey());
|
||||
rejectReset(nodeJson.getNodeCode(), skipNextMap, nodeMap);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.service;
|
||||
import org.dromara.warm.flow.json.JsonUtil;
|
||||
|
||||
import org.dromara.warm.flow.entity.*;
|
||||
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.constant.ExceptionCons;
|
||||
import org.dromara.warm.flow.dto.DefJson;
|
||||
import org.dromara.warm.flow.dto.FlowCombine;
|
||||
import org.dromara.warm.flow.entity.FlowDefinition;
|
||||
import org.dromara.warm.flow.entity.FlowInstance;
|
||||
import org.dromara.warm.flow.entity.FlowNode;
|
||||
import org.dromara.warm.flow.entity.FlowSkip;
|
||||
import org.dromara.warm.flow.enums.ActivityStatus;
|
||||
import org.dromara.warm.flow.enums.PublishStatus;
|
||||
import org.dromara.warm.flow.exception.FlowException;
|
||||
import org.dromara.warm.flow.service.WarmServiceImpl;
|
||||
import org.dromara.warm.flow.utils.*;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.dromara.warm.flow.mapper.FlowDefinitionMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 流程定义Service业务层处理
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class DefService extends WarmServiceImpl<FlowDefinition> {
|
||||
public FlowDefinition importIs(InputStream is) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is))) {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
stringBuilder.append(line);
|
||||
stringBuilder.append(System.lineSeparator());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new FlowException(ExceptionCons.READ_IS_ERROR);
|
||||
}
|
||||
return importJson(stringBuilder.toString());
|
||||
}
|
||||
|
||||
public FlowDefinition importJson(String defJson) {
|
||||
return importDef(JsonUtil.strToBean(defJson, DefJson.class));
|
||||
}
|
||||
|
||||
public FlowDefinition importDef(DefJson defJson) {
|
||||
FlowDefinition definition = DefJson.copyDef(defJson);
|
||||
FlowCombine flowCombine = FlowConfigUtil.structureFlow(definition);
|
||||
return insertFlow(flowCombine.getDefinition(), flowCombine.getAllNodes(), flowCombine.getAllSkips());
|
||||
}
|
||||
|
||||
public FlowDefinition insertFlow(FlowDefinition definition, List<FlowNode> nodeList, List<FlowSkip> skipList) {
|
||||
definition.setVersion(getNewVersion(definition));
|
||||
for (FlowNode node : nodeList) {
|
||||
node.setVersion(definition.getVersion());
|
||||
}
|
||||
FlowEngine.defService().save(definition);
|
||||
FlowEngine.nodeService().saveBatch(nodeList);
|
||||
FlowEngine.skipService().saveBatch(skipList);
|
||||
return definition;
|
||||
}
|
||||
|
||||
public boolean checkAndSave(FlowDefinition definition) {
|
||||
return save(definition.setVersion(getNewVersion(definition)));
|
||||
}
|
||||
|
||||
public void saveDef(DefJson defJson, boolean onlyNodeSkip) {
|
||||
if (ObjectUtil.isNull(defJson)) {
|
||||
return;
|
||||
}
|
||||
FlowCombine flowCombine = DefJson.copyCombine(defJson);
|
||||
FlowDefinition definition = flowCombine.getDefinition();
|
||||
Long id = definition.getId();
|
||||
// 如果是新增的流程定义
|
||||
if (ObjectUtil.isNull(id)) {
|
||||
definition.setVersion(getNewVersion(definition));
|
||||
FlowEngine.dataFillHandler().idFill(definition);
|
||||
}
|
||||
|
||||
// 校验流程定义合法性
|
||||
checkFlowLegal(flowCombine);
|
||||
|
||||
// 如果是新增的流程定义
|
||||
if (ObjectUtil.isNull(id)) {
|
||||
FlowEngine.defService().save(definition);
|
||||
} else {
|
||||
if (!onlyNodeSkip) {
|
||||
FlowEngine.defService().updateById(definition);
|
||||
}
|
||||
// 删除所有节点和连线
|
||||
FlowEngine.nodeService().remove(new FlowNode().setDefinitionId(id));
|
||||
FlowEngine.skipService().remove(new FlowSkip().setDefinitionId(id));
|
||||
}
|
||||
|
||||
// 保存流程节点和跳转
|
||||
List<FlowNode> allNodes = flowCombine.getAllNodes();
|
||||
allNodes.forEach(node -> {
|
||||
if(StrUtil.isEmpty(node.getNodeRatio())) {
|
||||
node.setNodeRatio("0");
|
||||
}
|
||||
});
|
||||
// 所有的流程连线
|
||||
List<FlowSkip> allSkips = flowCombine.getAllSkips();
|
||||
|
||||
// 保存节点,流程连线,权利人
|
||||
FlowEngine.nodeService().saveBatch(allNodes);
|
||||
FlowEngine.skipService().saveBatch(allSkips);
|
||||
}
|
||||
|
||||
public String exportJson(Long id) {
|
||||
return JsonUtil.objToStr(queryDesign(id).setIsPublish(null));
|
||||
}
|
||||
|
||||
public FlowDefinition getAllDataDefinition(Long id) {
|
||||
FlowDefinition definition = getMapper().selectById(id);
|
||||
List<FlowNode> nodeList = FlowEngine.nodeService().getByDefId(id);
|
||||
definition.setNodeList(nodeList);
|
||||
List<FlowSkip> skips = FlowEngine.skipService().getByDefId(id);
|
||||
Map<String, List<FlowSkip>> flowSkipMap = skips.stream()
|
||||
.collect(Collectors.groupingBy(FlowSkip::getNowNodeCode));
|
||||
nodeList.forEach(flowNode -> flowNode.setSkipList(flowSkipMap.get(flowNode.getNodeCode())));
|
||||
return definition;
|
||||
}
|
||||
|
||||
public FlowCombine getFlowCombine(Long id) {
|
||||
return getFlowCombine(getMapper().selectById(id));
|
||||
}
|
||||
|
||||
public FlowCombine getFlowCombineNoDef(Long id) {
|
||||
FlowCombine flowCombine = new FlowCombine();
|
||||
flowCombine.setAllNodes(FlowEngine.nodeService().getByDefId(id));
|
||||
flowCombine.setAllSkips(FlowEngine.skipService().getByDefId(id));
|
||||
return flowCombine;
|
||||
}
|
||||
|
||||
public FlowCombine getFlowCombine(FlowDefinition definition) {
|
||||
FlowCombine flowCombine = getFlowCombineNoDef(definition.getId());
|
||||
flowCombine.setDefinition(definition);
|
||||
return flowCombine;
|
||||
}
|
||||
|
||||
public DefJson queryDesign(Long id) {
|
||||
return DefJson.copyDef(getAllDataDefinition(id));
|
||||
}
|
||||
|
||||
public List<FlowDefinition> queryByCodeList(List<String> flowCodeList) {
|
||||
return getMapper().queryByCodeList(flowCodeList);
|
||||
}
|
||||
|
||||
public void updatePublishStatus(List<Long> ids, Integer publishStatus) {
|
||||
getMapper().updatePublishStatus(ids, publishStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除流程定义
|
||||
*
|
||||
* @param ids 流程定义id
|
||||
*/
|
||||
public boolean removeDef(List<Long> ids) {
|
||||
ids.forEach(id -> {
|
||||
List<FlowInstance> instances = FlowEngine.insService().getByDefId(id);
|
||||
AssertUtil.isNotEmpty(instances, ExceptionCons.EXIST_START_TASK);
|
||||
});
|
||||
FlowEngine.nodeService().deleteNodeByDefIds(ids);
|
||||
FlowEngine.skipService().deleteSkipByDefIds(ids);
|
||||
return removeByIds(ids);
|
||||
}
|
||||
|
||||
public boolean publish(Long id) {
|
||||
List<FlowNode> nodeList = FlowEngine.nodeService().getByDefId(id);
|
||||
AssertUtil.isEmpty(nodeList, ExceptionCons.NOT_DRAW_FLOW_ERROR);
|
||||
FlowDefinition definition = getById(id);
|
||||
List<FlowDefinition> definitions = getByFlowCode(definition.getFlowCode());
|
||||
// 已发布流程定义,改为已失效或者未发布状态
|
||||
List<Long> otherDefIds = definitions.stream()
|
||||
.filter(item -> !Objects.equals(definition.getId(), item.getId())
|
||||
&& PublishStatus.PUBLISHED.getKey().equals(item.getIsPublish()))
|
||||
.map(FlowDefinition::getId)
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(otherDefIds)) {
|
||||
List<FlowInstance> instanceList = FlowEngine.insService().listByDefIds(otherDefIds);
|
||||
if (CollUtil.isNotEmpty(instanceList)) {
|
||||
// 已发布已使用过的流程定义
|
||||
Set<Long> useDefIds = StreamUtils.toSet(instanceList, FlowInstance::getDefinitionId);
|
||||
if (CollUtil.isNotEmpty(useDefIds)) {
|
||||
// 已发布已使用过的流程定义,改为已失效
|
||||
getMapper().updatePublishStatus(new ArrayList<>(useDefIds), PublishStatus.EXPIRED.getKey());
|
||||
// 过滤掉已发布已使用-->已发布未使用
|
||||
otherDefIds.removeIf(useDefIds::contains);
|
||||
}
|
||||
}
|
||||
if (CollUtil.isNotEmpty(otherDefIds)) {
|
||||
// 已发布未使用过的流程定义,改为未发布
|
||||
getMapper().updatePublishStatus(otherDefIds, PublishStatus.UNPUBLISHED.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
FlowDefinition flowDefinition = new FlowDefinition();
|
||||
flowDefinition.setId(id);
|
||||
flowDefinition.setIsPublish(PublishStatus.PUBLISHED.getKey());
|
||||
return updateById(flowDefinition);
|
||||
}
|
||||
|
||||
public boolean unPublish(Long id) {
|
||||
List<FlowInstance> instances = FlowEngine.insService().getByDefId(id);
|
||||
AssertUtil.isNotEmpty(instances, ExceptionCons.EXIST_START_TASK);
|
||||
FlowDefinition definition = new FlowDefinition().setId(id);
|
||||
definition.setIsPublish(PublishStatus.UNPUBLISHED.getKey());
|
||||
return updateById(definition);
|
||||
}
|
||||
|
||||
public boolean copyDef(Long id) {
|
||||
FlowDefinition sourceDef = getById(id);
|
||||
AssertUtil.isNull(sourceDef, ExceptionCons.NOT_FOUNT_DEF);
|
||||
FlowDefinition definition = sourceDef.copy();
|
||||
definition.setVersion(getNewVersion(definition));
|
||||
|
||||
List<FlowNode> nodeList = FlowEngine.nodeService().getByDefId(id).stream().map(FlowNode::copy).collect(Collectors.toList());
|
||||
List<FlowSkip> skipList = FlowEngine.skipService().getByDefId(id).stream().map(FlowSkip::copy).collect(Collectors.toList());
|
||||
FlowEngine.dataFillHandler().idFill(definition);
|
||||
|
||||
nodeList.forEach(node -> node.setDefinitionId(definition.getId()).setVersion(definition.getVersion()));
|
||||
FlowEngine.nodeService().saveBatch(nodeList);
|
||||
|
||||
skipList.forEach(skip -> skip.setDefinitionId(definition.getId()));
|
||||
FlowEngine.skipService().saveBatch(skipList);
|
||||
return save(definition);
|
||||
}
|
||||
|
||||
public boolean active(Long id) {
|
||||
FlowDefinition definition = getById(id);
|
||||
AssertUtil.isNull(definition, ExceptionCons.NOT_FOUNT_DEF);
|
||||
AssertUtil.isTrue(ActivityStatus.isActivity(definition.getActivityStatus()), ExceptionCons.DEFINITION_ALREADY_ACTIVITY);
|
||||
definition.setActivityStatus(ActivityStatus.ACTIVITY.getKey());
|
||||
return updateById(definition);
|
||||
}
|
||||
|
||||
public boolean unActive(Long id) {
|
||||
FlowDefinition definition = getById(id);
|
||||
AssertUtil.isNull(definition, ExceptionCons.NOT_FOUNT_DEF);
|
||||
AssertUtil.isTrue(ActivityStatus.isSuspended(definition.getActivityStatus()), ExceptionCons.DEFINITION_ALREADY_SUSPENDED);
|
||||
definition.setActivityStatus(ActivityStatus.SUSPENDED.getKey());
|
||||
return updateById(definition);
|
||||
}
|
||||
|
||||
public List<FlowDefinition> getByFlowCode(String flowCode) {
|
||||
return list(new FlowDefinition().setFlowCode(flowCode));
|
||||
}
|
||||
|
||||
public FlowDefinition getPublishByFlowCode(String flowCode) {
|
||||
return FlowEngine.defService().getOne(new FlowDefinition()
|
||||
.setFlowCode(flowCode).setIsPublish(PublishStatus.PUBLISHED.getKey()));
|
||||
}
|
||||
|
||||
private String getNewVersion(FlowDefinition definition) {
|
||||
List<String> flowCodeList = Collections.singletonList(definition.getFlowCode());
|
||||
List<FlowDefinition> definitions = getMapper().queryByCodeList(flowCodeList);
|
||||
int highestVersion = 0;
|
||||
String latestNonPositiveVersion = null;
|
||||
long latestTimestamp = Long.MIN_VALUE;
|
||||
|
||||
for (FlowDefinition otherDef : definitions) {
|
||||
if (definition.getFlowCode().equals(otherDef.getFlowCode())) {
|
||||
try {
|
||||
int version = Integer.parseInt(otherDef.getVersion());
|
||||
if (version > highestVersion) {
|
||||
highestVersion = version;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
long timestamp = otherDef.getCreateTime().getTime();
|
||||
if (timestamp > latestTimestamp) {
|
||||
latestTimestamp = timestamp;
|
||||
latestNonPositiveVersion = otherDef.getVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String version = "1";
|
||||
if (highestVersion > 0) {
|
||||
version = String.valueOf(highestVersion + 1);
|
||||
} else if (latestNonPositiveVersion != null) {
|
||||
version = latestNonPositiveVersion + "_1";
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
private void checkFlowLegal(FlowCombine flowCombine) {
|
||||
FlowDefinition definition = flowCombine.getDefinition();
|
||||
String flowName = definition.getFlowName();
|
||||
AssertUtil.isEmpty(definition.getFlowCode(), "【" + flowName + "】流程flowCode为空!");
|
||||
// 节点校验
|
||||
List<FlowNode> allNodes = flowCombine.getAllNodes();
|
||||
List<FlowSkip> allSkips = flowCombine.getAllSkips();
|
||||
Map<String, List<FlowSkip>> skipMap = StreamUtils.groupByKey(allSkips, FlowSkip::getNowNodeCode);
|
||||
allNodes.forEach(node -> {
|
||||
node.setSkipList(skipMap.get(node.getNodeCode()));
|
||||
skipMap.remove(node.getNodeCode());
|
||||
});
|
||||
AssertUtil.isNotEmpty(skipMap, "[" + flowName + "]" + ExceptionCons.FLOW_HAVE_USELESS_SKIP);
|
||||
// 每一个流程的开始节点个数
|
||||
Set<String> nodeCodeSet = new HashSet<>();
|
||||
// 便利一个流程中的各个节点
|
||||
int startNum = 0;
|
||||
for (FlowNode node : allNodes) {
|
||||
FlowConfigUtil.initNodeAndCondition(node, definition.getId(), definition.getVersion());
|
||||
startNum = FlowConfigUtil.checkStartAndSame(node, startNum, flowName, nodeCodeSet);
|
||||
}
|
||||
AssertUtil.isTrue(startNum == 0, "[" + flowName + "]" + ExceptionCons.LOST_START_NODE);
|
||||
// 校验跳转节点的合法性
|
||||
FlowConfigUtil.checkSkipNode(allSkips);
|
||||
// 校验所有目标节点是否都存在
|
||||
FlowConfigUtil.validaIsExistDestNode(allSkips, nodeCodeSet);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public FlowDefinitionMapper getMapper() {
|
||||
return SpringUtils.getBean(FlowDefinitionMapper.class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.service;
|
||||
import org.dromara.warm.flow.entity.*;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.dto.FlowParams;
|
||||
import org.dromara.warm.flow.entity.FlowHisTask;
|
||||
import org.dromara.warm.flow.entity.FlowNode;
|
||||
import org.dromara.warm.flow.entity.FlowTask;
|
||||
import org.dromara.warm.flow.entity.FlowUser;
|
||||
import org.dromara.warm.flow.enums.CooperateType;
|
||||
import org.dromara.warm.flow.enums.FlowStatus;
|
||||
import org.dromara.warm.flow.enums.SkipType;
|
||||
import org.dromara.warm.flow.service.WarmServiceImpl;
|
||||
import org.dromara.warm.flow.utils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.dromara.warm.flow.mapper.FlowHisTaskMapper;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 历史任务记录Service业务层处理
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
@Service
|
||||
public class HisTaskService extends WarmServiceImpl<FlowHisTask> {
|
||||
public List<FlowHisTask> listByTaskId(Long taskId) {
|
||||
return list(new FlowHisTask().setTaskId(taskId));
|
||||
}
|
||||
|
||||
public List<FlowHisTask> listByTaskIdAndCooperateTypes(Long taskId, Integer... cooperateTypes) {
|
||||
if (ArrayUtil.isEmpty(cooperateTypes)) {
|
||||
return listByTaskId(taskId);
|
||||
}
|
||||
if (cooperateTypes.length == 1) {
|
||||
return list(new FlowHisTask().setTaskId(taskId).setCooperateType(cooperateTypes[0]));
|
||||
}
|
||||
return getMapper().listByTaskIdAndCooperateTypes(taskId, cooperateTypes);
|
||||
}
|
||||
|
||||
public List<FlowHisTask> getByInsAndNodeCodes(Long instanceId, List<String> nodeCodes) {
|
||||
return getMapper().getByInsAndNodeCodes(instanceId, nodeCodes);
|
||||
}
|
||||
|
||||
public boolean deleteByInsIds(List<Long> instanceIds) {
|
||||
return SqlHelper.retBool(getMapper().deleteByInsIds(instanceIds));
|
||||
}
|
||||
|
||||
public FlowHisTask setSkipInsHis(FlowTask task, List<FlowNode> nextNodes, FlowParams flowParams) {
|
||||
String flowStatus = getFlowStatus(flowParams);
|
||||
return setSkipHis(task, nextNodes, flowParams, flowStatus);
|
||||
}
|
||||
|
||||
public List<FlowHisTask> setSkipHisList(List<FlowTask> taskList, List<FlowNode> nextNodes, FlowParams flowParams) {
|
||||
String flowStatus = getFlowStatus(flowParams);
|
||||
List<FlowHisTask> hisTasks = new ArrayList<>();
|
||||
for (FlowTask task : taskList) {
|
||||
FlowHisTask hisTask = setSkipHis(task, nextNodes, flowParams, flowStatus);
|
||||
hisTasks.add(hisTask);
|
||||
}
|
||||
return hisTasks;
|
||||
}
|
||||
|
||||
public FlowHisTask setSkipHisTask(FlowTask task, FlowNode nextNode, FlowParams flowParams) {
|
||||
String flowStatus = getFlowStatus(flowParams);
|
||||
return setSkipHis(task, CollUtil.toList(nextNode), flowParams, flowStatus);
|
||||
}
|
||||
|
||||
|
||||
public FlowHisTask setCooperateHis(FlowTask task, FlowParams flowParams
|
||||
, List<String> collaborators) {
|
||||
String flowStatus = getFlowStatus(flowParams);
|
||||
FlowHisTask hisTask = new FlowHisTask()
|
||||
.setTaskId(task.getId())
|
||||
.setInstanceId(task.getInstanceId())
|
||||
.setCooperateType(ObjectUtil.defaultIfNull(flowParams.getCooperateType(), CooperateType.APPROVAL.getKey()))
|
||||
.setCollaborator(StreamUtils.join(collaborators, c -> c))
|
||||
.setNodeCode(task.getNodeCode())
|
||||
.setNodeName(task.getNodeName())
|
||||
.setNodeType(task.getNodeType())
|
||||
.setDefinitionId(task.getDefinitionId())
|
||||
.setTargetNodeCode(task.getNodeCode())
|
||||
.setTargetNodeName(task.getNodeName())
|
||||
.setApprover(flowParams.getHandler())
|
||||
.setSkipType(flowParams.getSkipType())
|
||||
.setFlowStatus(StrUtil.emptyToDefault(flowStatus, FlowStatus.APPROVAL.getKey()))
|
||||
.setFormCustom(task.getFormCustom())
|
||||
.setFormPath(task.getFormPath())
|
||||
.setMessage(flowParams.getMessage())
|
||||
.setVariable(flowParams.getVariableStr())
|
||||
//业务详情添加至历史记录
|
||||
.setExt(flowParams.getHisTaskExt())
|
||||
.setCreateTime(task.getCreateTime());
|
||||
FlowEngine.dataFillHandler().idFill(hisTask);
|
||||
return hisTask;
|
||||
}
|
||||
|
||||
public FlowHisTask notSkip(FlowTask task, FlowParams flowParams) {
|
||||
String flowStatus = getFlowStatus(flowParams);
|
||||
FlowHisTask hisTask = new FlowHisTask()
|
||||
.setTaskId(task.getId())
|
||||
.setInstanceId(task.getInstanceId())
|
||||
.setCooperateType(ObjectUtil.defaultIfNull(flowParams.getCooperateType(), CooperateType.APPROVAL.getKey()))
|
||||
.setNodeCode(task.getNodeCode())
|
||||
.setNodeName(task.getNodeName())
|
||||
.setNodeType(task.getNodeType())
|
||||
.setDefinitionId(task.getDefinitionId())
|
||||
.setTargetNodeCode(task.getNodeCode())
|
||||
.setTargetNodeName(task.getNodeName())
|
||||
.setApprover(flowParams.getHandler())
|
||||
.setSkipType(SkipType.NONE.getKey())
|
||||
.setFlowStatus(flowStatus)
|
||||
.setFormCustom(task.getFormCustom())
|
||||
.setFormPath(task.getFormPath())
|
||||
.setMessage(flowParams.getMessage())
|
||||
.setVariable(flowParams.getVariableStr())
|
||||
//业务详情添加至历史记录
|
||||
.setExt(flowParams.getHisTaskExt())
|
||||
.setCreateTime(task.getCreateTime());
|
||||
FlowEngine.dataFillHandler().idFill(hisTask);
|
||||
return hisTask;
|
||||
}
|
||||
|
||||
public FlowHisTask setDeputeHisTask(FlowTask task, FlowParams flowParams, FlowUser entrustedUser) {
|
||||
String flowStatus = getFlowStatus(flowParams);
|
||||
FlowHisTask hisTask = new FlowHisTask()
|
||||
.setTaskId(task.getId())
|
||||
.setInstanceId(task.getInstanceId())
|
||||
.setCooperateType(CooperateType.DEPUTE.getKey())
|
||||
.setNodeCode(task.getNodeCode())
|
||||
.setNodeName(task.getNodeName())
|
||||
.setNodeType(task.getNodeType())
|
||||
.setDefinitionId(task.getDefinitionId())
|
||||
.setTargetNodeCode(task.getNodeCode())
|
||||
.setTargetNodeName(task.getNodeName())
|
||||
.setApprover(flowParams.getHandler())
|
||||
.setCollaborator(entrustedUser.getCreateBy())
|
||||
.setSkipType(flowParams.getSkipType())
|
||||
.setFlowStatus(StrUtil.isNotEmpty(flowStatus)
|
||||
? flowStatus : SkipType.isReject(flowParams.getSkipType())
|
||||
? FlowStatus.REJECT.getKey() : FlowStatus.PASS.getKey())
|
||||
.setFormCustom(task.getFormCustom())
|
||||
.setFormPath(task.getFormPath())
|
||||
.setMessage(flowParams.getMessage())
|
||||
.setVariable(flowParams.getVariableStr())
|
||||
//业务详情添加至历史记录
|
||||
.setExt(flowParams.getHisTaskExt())
|
||||
.setCreateTime(task.getCreateTime());
|
||||
FlowEngine.dataFillHandler().idFill(hisTask);
|
||||
return hisTask;
|
||||
}
|
||||
|
||||
public FlowHisTask setSignHisTask(FlowTask task, FlowParams flowParams, String nodeRatio, boolean isPass) {
|
||||
String flowStatus = getFlowStatus(flowParams);
|
||||
FlowHisTask hisTask = new FlowHisTask()
|
||||
.setTaskId(task.getId())
|
||||
.setInstanceId(task.getInstanceId())
|
||||
.setCooperateType(CooperateType.isCountersign(nodeRatio)
|
||||
? CooperateType.COUNTERSIGN.getKey() : CooperateType.VOTE.getKey())
|
||||
.setNodeCode(task.getNodeCode())
|
||||
.setNodeName(task.getNodeName())
|
||||
.setNodeType(task.getNodeType())
|
||||
.setDefinitionId(task.getDefinitionId())
|
||||
.setApprover(flowParams.getHandler())
|
||||
.setSkipType(isPass ? SkipType.PASS.getKey() : SkipType.REJECT.getKey())
|
||||
.setFlowStatus(StrUtil.isNotEmpty(flowStatus)
|
||||
? flowStatus : isPass
|
||||
? FlowStatus.PASS.getKey() : FlowStatus.REJECT.getKey())
|
||||
.setFormCustom(task.getFormCustom())
|
||||
.setFormPath(task.getFormPath())
|
||||
.setMessage(flowParams.getMessage())
|
||||
.setVariable(flowParams.getVariableStr())
|
||||
//业务详情添加至历史记录
|
||||
.setExt(flowParams.getHisTaskExt())
|
||||
.setCreateTime(task.getCreateTime());
|
||||
FlowEngine.dataFillHandler().idFill(hisTask);
|
||||
return hisTask;
|
||||
}
|
||||
|
||||
public List<FlowHisTask> getByInsId(Long instanceId) {
|
||||
return FlowEngine.hisTaskService().list(new FlowHisTask().setInstanceId(instanceId));
|
||||
}
|
||||
|
||||
private FlowHisTask setSkipHis(FlowTask task, List<FlowNode> nextNodes, FlowParams flowParams, String flowStatus) {
|
||||
FlowHisTask hisTask = new FlowHisTask()
|
||||
.setTaskId(task.getId())
|
||||
.setInstanceId(task.getInstanceId())
|
||||
.setCooperateType(ObjectUtil.defaultIfNull(flowParams.getCooperateType(), CooperateType.APPROVAL.getKey()))
|
||||
.setNodeCode(task.getNodeCode())
|
||||
.setNodeName(task.getNodeName())
|
||||
.setNodeType(task.getNodeType())
|
||||
.setDefinitionId(task.getDefinitionId())
|
||||
.setTargetNodeCode(StreamUtils.join(nextNodes, FlowNode::getNodeCode))
|
||||
.setTargetNodeName(StreamUtils.join(nextNodes, FlowNode::getNodeName))
|
||||
.setApprover(flowParams.getHandler())
|
||||
.setSkipType(flowParams.getSkipType())
|
||||
.setFlowStatus(StrUtil.isNotEmpty(flowStatus)
|
||||
? flowStatus : SkipType.isReject(flowParams.getSkipType())
|
||||
? FlowStatus.REJECT.getKey() : FlowStatus.PASS.getKey())
|
||||
.setFormCustom(task.getFormCustom())
|
||||
.setFormPath(task.getFormPath())
|
||||
.setMessage(flowParams.getMessage())
|
||||
.setVariable(flowParams.getVariableStr())
|
||||
//业务详情添加至历史记录
|
||||
.setExt(flowParams.getHisTaskExt())
|
||||
.setCreateTime(task.getCreateTime());
|
||||
FlowEngine.dataFillHandler().idFill(hisTask);
|
||||
return hisTask;
|
||||
}
|
||||
|
||||
private String getFlowStatus(FlowParams flowParams) {
|
||||
return StrUtil.emptyToDefault(flowParams.getHisStatus(), flowParams.getFlowStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlowHisTaskMapper getMapper() {
|
||||
return SpringUtils.getBean(FlowHisTaskMapper.class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.service;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Service接口
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-17
|
||||
*/
|
||||
public interface IWarmService<T> {
|
||||
/**
|
||||
* 根据id查询
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 实体
|
||||
*/
|
||||
T getById(Serializable id);
|
||||
|
||||
/**
|
||||
* 根据ids查询
|
||||
*
|
||||
* @param ids 主键
|
||||
* @return 实体
|
||||
*/
|
||||
List<T> getByIds(Collection<? extends Serializable> ids);
|
||||
|
||||
/**
|
||||
* 查询列表
|
||||
*
|
||||
* @param entity 查询实体
|
||||
* @return 集合
|
||||
*/
|
||||
List<T> list(T entity);
|
||||
|
||||
/**
|
||||
* 查询一条记录
|
||||
*
|
||||
* @param entity 查询实体
|
||||
* @return 结果
|
||||
*/
|
||||
T getOne(T entity);
|
||||
|
||||
/**
|
||||
* 判断是否存在
|
||||
*
|
||||
* @param entity 查询实体
|
||||
* @return 结果
|
||||
*/
|
||||
Boolean exists(T entity);
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*
|
||||
* @param entity 实体
|
||||
* @return 结果
|
||||
*/
|
||||
boolean save(T entity);
|
||||
|
||||
/**
|
||||
* 根据id修改
|
||||
*
|
||||
* @param entity 实体
|
||||
* @return 结果
|
||||
*/
|
||||
boolean updateById(T entity);
|
||||
|
||||
/**
|
||||
* 根据id删除
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 结果
|
||||
*/
|
||||
boolean removeById(Serializable id);
|
||||
|
||||
/**
|
||||
* 根据entity删除
|
||||
*
|
||||
* @param entity 实体
|
||||
* @return 结果
|
||||
*/
|
||||
boolean remove(T entity);
|
||||
|
||||
/**
|
||||
* 根据ids批量删除
|
||||
*
|
||||
* @param ids 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
boolean removeByIds(Collection<? extends Serializable> ids);
|
||||
|
||||
/**
|
||||
* 批量新增
|
||||
*
|
||||
* @param list 实体集合
|
||||
*/
|
||||
void saveBatch(List<T> list);
|
||||
}
|
||||
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.service;
|
||||
import org.dromara.warm.flow.json.JsonUtil;
|
||||
|
||||
import org.dromara.warm.flow.entity.*;
|
||||
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.constant.ExceptionCons;
|
||||
import org.dromara.warm.flow.dto.FlowCombine;
|
||||
import org.dromara.warm.flow.dto.FlowParams;
|
||||
import org.dromara.warm.flow.dto.PathWayData;
|
||||
import org.dromara.warm.flow.enums.ActivityStatus;
|
||||
import org.dromara.warm.flow.enums.FlowStatus;
|
||||
import org.dromara.warm.flow.enums.NodeType;
|
||||
import org.dromara.warm.flow.enums.SkipType;
|
||||
import org.dromara.warm.flow.listener.ListenerVariable;
|
||||
import org.dromara.warm.flow.service.WarmServiceImpl;
|
||||
import org.dromara.warm.flow.utils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
import org.dromara.warm.flow.entity.FlowInstance;
|
||||
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.dromara.warm.flow.mapper.FlowInstanceMapper;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 流程实例Service业务层处理
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
@Service
|
||||
public class InsService extends WarmServiceImpl<FlowInstance> {
|
||||
public FlowInstance start(String businessId, FlowParams flowParams) {
|
||||
AssertUtil.isNull(flowParams.getFlowCode(), ExceptionCons.NULL_FLOW_CODE);
|
||||
AssertUtil.isEmpty(businessId, ExceptionCons.NULL_BUSINESS_ID);
|
||||
// 获取已发布的流程节点
|
||||
FlowDefinition definition = FlowEngine.defService().getPublishByFlowCode(flowParams.getFlowCode());
|
||||
AssertUtil.isNull(definition, ExceptionCons.NOT_FOUNT_DEF);
|
||||
FlowCombine flowCombine = FlowEngine.defService().getFlowCombine(definition);
|
||||
// 获取开始节点
|
||||
FlowNode startNode = StreamUtils.findFirstValue(flowCombine.getAllNodes(), t -> NodeType.isStart(t.getNodeType()));
|
||||
AssertUtil.isNull(startNode, ExceptionCons.LOST_START_NODE);
|
||||
|
||||
// 判断流程定义是否激活状态
|
||||
AssertUtil.isTrue(definition.getActivityStatus().equals(ActivityStatus.SUSPENDED.getKey())
|
||||
, ExceptionCons.NOT_DEFINITION_ACTIVITY);
|
||||
flowParams.skipType(SkipType.PASS.getKey());
|
||||
|
||||
// 执行开始监听器
|
||||
ListenerUtil.executeStart(new ListenerVariable(definition, null, startNode, flowParams.getVariable())
|
||||
.setFlowParams(flowParams));
|
||||
|
||||
|
||||
// 获取下一个节点,如果是网关节点,则重新获取后续节点
|
||||
PathWayData pathWayData = new PathWayData().setDefId(startNode.getDefinitionId()).setSkipType(flowParams.getSkipType());
|
||||
List<FlowNode> nextNodes = FlowEngine.nodeService().getNextNodeList(startNode, null, flowParams.getSkipType(),
|
||||
flowParams.getVariable(), pathWayData, flowCombine);
|
||||
|
||||
// 设置流程实例对象
|
||||
FlowInstance instance = setStartInstance(nextNodes.get(0), businessId, flowParams);
|
||||
|
||||
// 设置历史任务
|
||||
FlowHisTask hisTask = setHisTask(nextNodes, flowParams, startNode, instance.getId());
|
||||
|
||||
List<FlowTask> addTasks = StreamUtils.toList(nextNodes, node -> FlowEngine.taskService()
|
||||
.addTask(node, instance, definition, flowParams));
|
||||
|
||||
// 办理人变量替换
|
||||
if (CollUtil.isNotEmpty(addTasks)) {
|
||||
ExpressionUtil.evalVariable(addTasks, flowParams);
|
||||
}
|
||||
|
||||
// 设置流程图元数据
|
||||
pathWayData.getTargetNodes().addAll(nextNodes);
|
||||
instance.setDefJson(FlowEngine.chartService().startMetadata(pathWayData));
|
||||
|
||||
// 执行分派监听器
|
||||
ListenerUtil.executeAssignment(new ListenerVariable(definition, instance, startNode, flowParams.getVariable()
|
||||
, null, nextNodes, addTasks).setFlowParams(flowParams));
|
||||
|
||||
// 开启流程,保存流程信息
|
||||
saveFlowInfo(instance, addTasks, hisTask, flowParams);
|
||||
|
||||
// 执行完成和创建监听器
|
||||
ListenerUtil.endCreateListener(new ListenerVariable(definition, instance, startNode, flowParams.getVariable()
|
||||
, null, nextNodes, addTasks).setFlowParams(flowParams));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public List<FlowInstance> listByDefIds(List<Long> defIds) {
|
||||
return getMapper().getByDefIds(defIds);
|
||||
}
|
||||
|
||||
public boolean remove(List<Long> instanceIds) {
|
||||
return toRemoveTask(instanceIds);
|
||||
}
|
||||
|
||||
public List<FlowInstance> getByDefId(Long definitionId) {
|
||||
return list(new FlowInstance().setDefinitionId(definitionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置历史任务
|
||||
*
|
||||
* @param nextNodes 下一节点集合
|
||||
* @param flowParams 流程参数
|
||||
* @param startNode 开始节点
|
||||
* @param instanceId 流程实例id
|
||||
*/
|
||||
private FlowHisTask setHisTask(List<FlowNode> nextNodes, FlowParams flowParams, FlowNode startNode, Long instanceId) {
|
||||
FlowTask startTask = new FlowTask()
|
||||
.setInstanceId(instanceId)
|
||||
.setDefinitionId(startNode.getDefinitionId())
|
||||
.setNodeCode(startNode.getNodeCode())
|
||||
.setNodeName(startNode.getNodeName())
|
||||
.setNodeType(startNode.getNodeType());
|
||||
FlowEngine.dataFillHandler().idFill(startTask);
|
||||
// 开始任务转历史任务
|
||||
return FlowEngine.hisTaskService().setSkipInsHis(startTask, nextNodes, flowParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启流程,保存流程信息
|
||||
*
|
||||
* @param instance 流程实例
|
||||
* @param addTasks 新增任务
|
||||
* @param hisTask 历史任务
|
||||
*/
|
||||
private void saveFlowInfo(FlowInstance instance, List<FlowTask> addTasks, FlowHisTask hisTask, FlowParams flowParams) {
|
||||
FlowEngine.taskService().setInsFinishInfo(instance, addTasks, flowParams);
|
||||
FlowEngine.hisTaskService().save(hisTask);
|
||||
// 待办任务设置处理人
|
||||
if (CollUtil.isNotEmpty(addTasks)) {
|
||||
List<FlowUser> users = FlowEngine.userService().taskAddUsers(addTasks);
|
||||
FlowEngine.taskService().saveBatch(addTasks);
|
||||
FlowEngine.userService().saveBatch(users);
|
||||
}
|
||||
save(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置流程实例对象
|
||||
*
|
||||
* @param firstBetweenNode 第一个中间节点
|
||||
* @param businessId 业务id
|
||||
* @return FlowInstance
|
||||
*/
|
||||
private FlowInstance setStartInstance(FlowNode firstBetweenNode, String businessId
|
||||
, FlowParams flowParams) {
|
||||
FlowInstance instance = new FlowInstance();
|
||||
Date now = new Date();
|
||||
FlowEngine.dataFillHandler().idFill(instance);
|
||||
// 关联业务id,其实后面可以不用到业务id,传业务id目前来看只是为了批量创建流程的时候能创建出有区别化的流程,也是为了后期需要用到businessId。
|
||||
instance.setDefinitionId(firstBetweenNode.getDefinitionId())
|
||||
.setBusinessId(businessId)
|
||||
.setNodeType(firstBetweenNode.getNodeType())
|
||||
.setNodeCode(firstBetweenNode.getNodeCode())
|
||||
.setNodeName(firstBetweenNode.getNodeName())
|
||||
.setFlowStatus(StrUtil.emptyToDefault(flowParams.getFlowStatus(), FlowStatus.TOBESUBMIT.getKey()))
|
||||
.setActivityStatus(ActivityStatus.ACTIVITY.getKey())
|
||||
.setVariable(JsonUtil.objToStr(flowParams.getVariable()))
|
||||
.setCreateTime(now)
|
||||
.setUpdateTime(now)
|
||||
.setCreateBy(flowParams.getHandler())
|
||||
.setUpdateBy(flowParams.getHandler())
|
||||
.setExt(flowParams.getExt());
|
||||
return instance;
|
||||
}
|
||||
|
||||
private boolean toRemoveTask(List<Long> instanceIds) {
|
||||
AssertUtil.isEmpty(instanceIds, ExceptionCons.NULL_INSTANCE_ID);
|
||||
|
||||
List<Long> taskIds = new ArrayList<>();
|
||||
instanceIds.forEach(instanceId -> taskIds.addAll(
|
||||
FlowEngine.taskService()
|
||||
.list(new FlowTask().setInstanceId(instanceId))
|
||||
.stream()
|
||||
.map(FlowTask::getId)
|
||||
.collect(Collectors.toList())));
|
||||
|
||||
if (CollUtil.isNotEmpty(taskIds)) {
|
||||
FlowEngine.userService().deleteByTaskIds(taskIds);
|
||||
}
|
||||
|
||||
FlowEngine.taskService().deleteByInsIds(instanceIds);
|
||||
FlowEngine.hisTaskService().deleteByInsIds(instanceIds);
|
||||
return FlowEngine.insService().removeByIds(instanceIds);
|
||||
}
|
||||
|
||||
public boolean active(Long id) {
|
||||
FlowInstance instance = getById(id);
|
||||
AssertUtil.isNull(instance, ExceptionCons.NOT_FOUNT_INSTANCE);
|
||||
AssertUtil.isTrue(ActivityStatus.isActivity(instance.getActivityStatus()), ExceptionCons.INSTANCE_ALREADY_ACTIVITY);
|
||||
instance.setActivityStatus(ActivityStatus.ACTIVITY.getKey());
|
||||
return updateById(instance);
|
||||
}
|
||||
|
||||
public boolean unActive(Long id) {
|
||||
FlowInstance instance = getById(id);
|
||||
AssertUtil.isNull(instance, ExceptionCons.NOT_FOUNT_INSTANCE);
|
||||
AssertUtil.isTrue(ActivityStatus.isSuspended(instance.getActivityStatus()), ExceptionCons.INSTANCE_ALREADY_SUSPENDED);
|
||||
instance.setActivityStatus(ActivityStatus.SUSPENDED.getKey());
|
||||
return updateById(instance);
|
||||
}
|
||||
|
||||
public void removeVariables(Long instanceId, String... keys) {
|
||||
FlowInstance instance = FlowEngine.insService().getById(instanceId);
|
||||
if (instance != null) {
|
||||
Map<String, Object> variableMap = instance.getVariableMap();
|
||||
for (String key : keys) {
|
||||
variableMap.remove(key);
|
||||
}
|
||||
instance.setVariable(JsonUtil.objToStr(variableMap));
|
||||
FlowEngine.insService().updateById(instance);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlowInstanceMapper getMapper() {
|
||||
return SpringUtils.getBean(FlowInstanceMapper.class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,366 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.service;
|
||||
import org.dromara.warm.flow.json.JsonUtil;
|
||||
|
||||
import org.dromara.warm.flow.entity.*;
|
||||
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.constant.ExceptionCons;
|
||||
import org.dromara.warm.flow.constant.FlowCons;
|
||||
import org.dromara.warm.flow.dto.FlowCombine;
|
||||
import org.dromara.warm.flow.dto.PathWayData;
|
||||
import org.dromara.warm.flow.entity.FlowDefinition;
|
||||
import org.dromara.warm.flow.entity.FlowNode;
|
||||
import org.dromara.warm.flow.entity.FlowSkip;
|
||||
import org.dromara.warm.flow.enums.NodeType;
|
||||
import org.dromara.warm.flow.enums.PublishStatus;
|
||||
import org.dromara.warm.flow.enums.SkipType;
|
||||
import org.dromara.warm.flow.exception.FlowException;
|
||||
import org.dromara.warm.flow.service.WarmServiceImpl;
|
||||
import org.dromara.warm.flow.utils.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
|
||||
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.dromara.warm.flow.mapper.FlowNodeMapper;
|
||||
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 流程节点Service业务层处理
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class NodeService extends WarmServiceImpl<FlowNode> {
|
||||
public List<FlowNode> getPublishByFlowCode(String flowCode) {
|
||||
FlowDefinition definition = FlowEngine.defService().getOne(new FlowDefinition()
|
||||
.setFlowCode(flowCode).setIsPublish(PublishStatus.PUBLISHED.getKey()));
|
||||
if (ObjectUtil.isNotNull(definition)) {
|
||||
return list(new FlowNode().setDefinitionId(definition.getId()));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public List<FlowNode> getByNodeCodes(List<String> nodeCodes, Long definitionId) {
|
||||
return getMapper().getByNodeCodes(nodeCodes, definitionId);
|
||||
}
|
||||
|
||||
public List<FlowNode> previousNodeList(Long nodeId) {
|
||||
FlowNode nowNode = getById(nodeId);
|
||||
return previousNodeList(nowNode.getDefinitionId(), nowNode.getNodeCode());
|
||||
}
|
||||
|
||||
public List<FlowNode> previousNodeList(Long definitionId, String nowNodeCode) {
|
||||
return prefixOrSuffixNodes(definitionId, nowNodeCode, FlowCons.PREVIOUS);
|
||||
}
|
||||
|
||||
public List<FlowNode> suffixNodeList(Long nodeId) {
|
||||
FlowNode nowNode = getById(nodeId);
|
||||
return suffixNodeList(nowNode.getDefinitionId(), nowNode.getNodeCode());
|
||||
}
|
||||
|
||||
public List<FlowNode> suffixNodeList(Long definitionId, String nowNodeCode) {
|
||||
return prefixOrSuffixNodes(definitionId, nowNodeCode, FlowCons.SUFFIX);
|
||||
}
|
||||
|
||||
public List<FlowNode> suffixNodeList(String nowNodeCode, FlowCombine flowCombine) {
|
||||
return prefixOrSuffixNodes(nowNodeCode, FlowCons.SUFFIX, flowCombine);
|
||||
}
|
||||
|
||||
public List<FlowNode> getByDefId(Long definitionId) {
|
||||
return list(new FlowNode().setDefinitionId(definitionId));
|
||||
}
|
||||
|
||||
public FlowNode getByDefIdAndNodeCode(Long definitionId, String nodeCode) {
|
||||
return getOne(new FlowNode().setDefinitionId(definitionId).setNodeCode(nodeCode));
|
||||
}
|
||||
|
||||
public FlowNode getStartNode(Long definitionId) {
|
||||
return getOne(new FlowNode().setDefinitionId(definitionId).setNodeType(NodeType.START.getKey()));
|
||||
}
|
||||
|
||||
public List<FlowNode> getBetweenNode(Long definitionId) {
|
||||
return list(new FlowNode().setDefinitionId(definitionId).setNodeType(NodeType.BETWEEN.getKey()));
|
||||
}
|
||||
|
||||
|
||||
public List<FlowNode> getFirstBetweenNode(Long definitionId, Map<String, Object> variable) {
|
||||
FlowCombine flowCombine = FlowEngine.defService().getFlowCombineNoDef(definitionId);
|
||||
FlowNode startNode = StreamUtils.findFirstValue(flowCombine.getAllNodes(), t -> NodeType.isStart(t.getNodeType()));
|
||||
return getNextNodeList(startNode, null, SkipType.PASS.getKey(),
|
||||
variable, null, flowCombine);
|
||||
}
|
||||
|
||||
public FlowNode getEndNode(Long definitionId) {
|
||||
return getOne(new FlowNode().setDefinitionId(definitionId).setNodeType(NodeType.END.getKey()));
|
||||
}
|
||||
|
||||
public List<FlowNode> prefixOrSuffixNodes(Long definitionId, String nowNodeCode, String type) {
|
||||
FlowCombine flowCombine = new FlowCombine();
|
||||
flowCombine.setAllNodes(FlowEngine.nodeService().getByDefId(definitionId));
|
||||
flowCombine.setAllSkips(FlowEngine.skipService().getByDefId(definitionId));
|
||||
return prefixOrSuffixNodes(nowNodeCode, type, flowCombine);
|
||||
}
|
||||
|
||||
public List<FlowNode> prefixOrSuffixNodes(String nowNodeCode, String type, FlowCombine flowCombine) {
|
||||
Map<String, FlowNode> nodeMap = StreamUtils.toMap(flowCombine.getAllNodes(), FlowNode::getNodeCode, node -> node);
|
||||
Map<String, List<FlowSkip>> skipMap = flowCombine.getAllSkips().stream().filter(skip -> SkipType.isPass(skip.getSkipType()))
|
||||
.collect(Collectors.groupingBy(FlowCons.PREVIOUS.equals(type) ? FlowSkip::getNextNodeCode : FlowSkip::getNowNodeCode
|
||||
, LinkedHashMap::new, Collectors.toList()));
|
||||
|
||||
List<FlowNode> prefixOrSuffixNodes = new ArrayList<>();
|
||||
List<String> prefixOrSuffixCode = prefixOrSuffixCodes(skipMap, nowNodeCode
|
||||
, FlowCons.PREVIOUS.equals(type) ? FlowSkip::getNowNodeCode : FlowSkip::getNextNodeCode);
|
||||
for (String nodeCode : prefixOrSuffixCode) {
|
||||
FlowNode node = nodeMap.get(nodeCode);
|
||||
if (!NodeType.isGateWay(node.getNodeType())) {
|
||||
prefixOrSuffixNodes.add(node);
|
||||
}
|
||||
}
|
||||
Collections.reverse(prefixOrSuffixNodes);
|
||||
Set<String> sameCode = new HashSet<>();
|
||||
prefixOrSuffixNodes.removeIf(node -> {
|
||||
if (sameCode.contains(node.getNodeCode())) {
|
||||
return true;
|
||||
}
|
||||
sameCode.add(node.getNodeCode());
|
||||
return false;
|
||||
});
|
||||
Collections.reverse(prefixOrSuffixNodes);
|
||||
return prefixOrSuffixNodes;
|
||||
}
|
||||
|
||||
public List<FlowNode> getNextNodeList(Long definitionId, String nowNodeCode, String anyNodeCode, String skipType,
|
||||
Map<String, Object> variable) {
|
||||
AssertUtil.isEmpty(nowNodeCode, ExceptionCons.LOST_NODE_CODE);
|
||||
// 查询当前节点
|
||||
FlowCombine flowCombine = FlowEngine.defService().getFlowCombineNoDef(definitionId);
|
||||
FlowNode nowNode = StreamUtils.findFirstValue(flowCombine.getAllNodes(), t -> t.getNodeCode().equals(nowNodeCode));
|
||||
// 如果是网关节点,则根据条件判断
|
||||
return getNextByCheckGateway(variable, getNextNode(nowNode, anyNodeCode, skipType, null, flowCombine),
|
||||
null, flowCombine);
|
||||
}
|
||||
|
||||
public FlowNode getNextNode(Long definitionId, String nowNodeCode, String anyNodeCode, String skipType) {
|
||||
// 查询当前节点
|
||||
FlowCombine flowCombine = FlowEngine.defService().getFlowCombineNoDef(definitionId);
|
||||
FlowNode nowNode = StreamUtils.findFirstValue(flowCombine.getAllNodes(), t -> t.getNodeCode().equals(nowNodeCode));
|
||||
return getNextNode(nowNode, anyNodeCode, skipType, null, flowCombine);
|
||||
}
|
||||
|
||||
public List<FlowNode> getNextNodeList(FlowNode nowNode, String anyNodeCode, String skipType, Map<String, Object> variable
|
||||
, PathWayData pathWayData, FlowCombine flowCombine) {
|
||||
// 如果是网关节点,则根据条件判断
|
||||
return getNextByCheckGateway(variable, getNextNode(nowNode, anyNodeCode, skipType
|
||||
, pathWayData, flowCombine), pathWayData, flowCombine);
|
||||
}
|
||||
|
||||
public FlowNode getNextNode(FlowNode nowNode, String anyNodeCode, String skipType, PathWayData pathWayData, FlowCombine flowCombine) {
|
||||
// 查询当前节点
|
||||
AssertUtil.isNull(nowNode, ExceptionCons.LOST_NODE_CODE);
|
||||
AssertUtil.isNull(nowNode.getDefinitionId(), ExceptionCons.NOT_DEFINITION_ID);
|
||||
AssertUtil.isEmpty(skipType, ExceptionCons.NULL_CONDITION_VALUE);
|
||||
|
||||
if (pathWayData != null) {
|
||||
pathWayData.getPathWayNodes().add(nowNode);
|
||||
}
|
||||
FlowNode nextNode = null;
|
||||
if (StrUtil.isNotEmpty(anyNodeCode)) {
|
||||
// 如果指定了跳转节点,直接获取节点
|
||||
nextNode = StreamUtils.findFirstValue(flowCombine.getAllNodes(), node -> anyNodeCode.equals(node.getNodeCode()));
|
||||
} else if (StrUtil.isNotEmpty(nowNode.getAnyNodeSkip()) && SkipType.isReject(skipType)) {
|
||||
// 如果配置了任意跳转节点,直接获取节点
|
||||
nextNode = StreamUtils.findFirstValue(flowCombine.getAllNodes(), node -> nowNode.getAnyNodeSkip().equals(node.getNodeCode()));
|
||||
}
|
||||
|
||||
if (ObjectUtil.isNotNull(nextNode)) {
|
||||
AssertUtil.isTrue(NodeType.isGateWay(nextNode.getNodeType()), ExceptionCons.TAR_NOT_GATEWAY);
|
||||
return nextNode;
|
||||
}
|
||||
|
||||
// 获取跳转关系
|
||||
List<FlowSkip> skips = StreamUtils.filter(flowCombine.getAllSkips(), skip -> nowNode.getNodeCode().equals(skip.getNowNodeCode()));
|
||||
AssertUtil.isNull(skips, ExceptionCons.NULL_DEST_NODE);
|
||||
FlowSkip nextSkip = getSkipByCheck(skips, skipType);
|
||||
|
||||
// 根据跳转查询出跳转到的那个节点
|
||||
nextNode = StreamUtils.findFirstValue(flowCombine.getAllNodes(), node -> nextSkip != null && nextSkip.getNextNodeCode().equals(node.getNodeCode()));
|
||||
AssertUtil.isNull(nextNode, ExceptionCons.NULL_NODE_CODE);
|
||||
AssertUtil.isTrue(NodeType.isStart(nextNode.getNodeType()), ExceptionCons.FIRST_FORBID_BACK);
|
||||
if (pathWayData != null) {
|
||||
pathWayData.getPathWayNodes().add(nextNode);
|
||||
pathWayData.getPathWaySkips().add(nextSkip);
|
||||
}
|
||||
return nextNode;
|
||||
}
|
||||
|
||||
public List<FlowNode> getNextByCheckGateway(Map<String, Object> variable, FlowNode nextNode, PathWayData pathWayData
|
||||
, FlowCombine flowCombine) {
|
||||
// 网关节点处理
|
||||
if (NodeType.isGateWay(nextNode.getNodeType())) {
|
||||
List<FlowSkip> skipsGateway = StreamUtils.filter(flowCombine.getAllSkips()
|
||||
, skip -> nextNode.getNodeCode().equals(skip.getNowNodeCode()));
|
||||
if (CollUtil.isEmpty(skipsGateway)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//如果是互斥网关,跳转条件匹配的,则取任意第一条,否则取跳转条件为空的任意一条
|
||||
if (NodeType.isGateWaySerial(nextNode.getNodeType())) {
|
||||
FlowSkip skipOne = null;
|
||||
for (FlowSkip skip : skipsGateway) {
|
||||
if (StrUtil.isNotEmpty(skip.getSkipCondition())) {
|
||||
if (ExpressionUtil.evalCondition(skip.getSkipCondition(), variable)) {
|
||||
skipOne = skip;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
skipOne = skip;
|
||||
}
|
||||
}
|
||||
skipsGateway = skipOne == null ? null : CollUtil.toList(skipOne);
|
||||
} else if (NodeType.isGateWayInclusive(nextNode.getNodeType())) {
|
||||
//如果是包含网关,有跳转条件的分支,但是跳转条件不匹配的不执行,没跳转条件为空的分支默认执行
|
||||
skipsGateway.removeIf(skip -> StrUtil.isNotEmpty(skip.getSkipCondition())
|
||||
&& !ExpressionUtil.evalCondition(skip.getSkipCondition(), variable));
|
||||
}
|
||||
|
||||
AssertUtil.isEmpty(skipsGateway, ExceptionCons.NULL_CONDITION_VALUE_NODE);
|
||||
List<String> nextNodeCodes = StreamUtils.toList(skipsGateway, FlowSkip::getNextNodeCode);
|
||||
List<FlowNode> nextNodes = StreamUtils.filter(flowCombine.getAllNodes()
|
||||
, node -> nextNodeCodes.contains(node.getNodeCode()));
|
||||
AssertUtil.isEmpty(nextNodes, ExceptionCons.NOT_NODE_DATA);
|
||||
if (pathWayData != null) {
|
||||
pathWayData.getPathWayNodes().addAll(nextNodes);
|
||||
pathWayData.getPathWaySkips().addAll(skipsGateway);
|
||||
}
|
||||
List<FlowNode> newNextNodes = new ArrayList<>();
|
||||
for (FlowNode node : nextNodes) {
|
||||
List<FlowNode> nodeList = getNextByCheckGateway(variable, node, pathWayData, flowCombine);
|
||||
newNextNodes.addAll(nodeList);
|
||||
}
|
||||
return newNextNodes;
|
||||
}
|
||||
// 非网关节点直接返回
|
||||
if (pathWayData != null) {
|
||||
pathWayData.getPathWayNodes().remove(nextNode);
|
||||
}
|
||||
AssertUtil.isTrue(NodeType.isStart(nextNode.getNodeType()), ExceptionCons.START_NODE_NOT_ALLOW_JUMP);
|
||||
return CollUtil.toList(nextNode);
|
||||
}
|
||||
|
||||
|
||||
public int deleteNodeByDefIds(Collection<? extends Serializable> defIds) {
|
||||
return getMapper().deleteNodeByDefIds(defIds);
|
||||
}
|
||||
|
||||
public Map<String, String> getExt(FlowNode node) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
String ext = node.getExt();
|
||||
if (StrUtil.isNotEmpty(ext)) {
|
||||
List<Map<String, Object>> extList = JsonUtil.strToList(ext);
|
||||
if (CollUtil.isNotEmpty(extList)) {
|
||||
for (Map<String, Object> extMap : extList) {
|
||||
String code = ObjectUtil.defaultIfNull(extMap.get("code"), "").toString();
|
||||
String value = ObjectUtil.defaultIfNull(extMap.get("value"), "").toString();
|
||||
if (StrUtil.isAllNotEmpty(code, value)) {
|
||||
map.put(code, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private List<String> prefixOrSuffixCodes(Map<String, List<FlowSkip>> skipMap, String nodeCode,
|
||||
Function<FlowSkip, String> supplier) {
|
||||
// 记录已访问节点,防止循环
|
||||
Set<String> visited = new HashSet<>();
|
||||
List<String> result = new ArrayList<>();
|
||||
prefixOrSuffixCodesRecursive(skipMap, nodeCode, supplier, visited, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void prefixOrSuffixCodesRecursive(Map<String, List<FlowSkip>> skipMap, String nodeCode,
|
||||
Function<FlowSkip, String> supplier, Set<String> visited, List<String> result) {
|
||||
if (visited.contains(nodeCode)) {
|
||||
// 防止循环访问
|
||||
return;
|
||||
}
|
||||
|
||||
visited.add(nodeCode);
|
||||
List<FlowSkip> skipList = skipMap.get(nodeCode);
|
||||
|
||||
if (CollUtil.isNotEmpty(skipList)) {
|
||||
for (FlowSkip skip : skipList) {
|
||||
if (SkipType.isPass(skip.getSkipType())) {
|
||||
String nextNodeCode = supplier.apply(skip);
|
||||
// 避免重复添加
|
||||
if (!result.contains(nextNodeCode)) {
|
||||
result.add(nextNodeCode);
|
||||
}
|
||||
prefixOrSuffixCodesRecursive(skipMap, nextNodeCode, supplier, visited, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过校验跳转类型获取跳转集合
|
||||
*
|
||||
* @param skips 跳转集合
|
||||
* @param skipType 跳转类型
|
||||
* @return List<FlowSkip>
|
||||
* @author xiarg
|
||||
* @since 2024/8/21 11:32
|
||||
*/
|
||||
private FlowSkip getSkipByCheck(List<FlowSkip> skips, String skipType) {
|
||||
return Optional.ofNullable(skips)
|
||||
.orElse(Collections.emptyList())
|
||||
.stream()
|
||||
.filter(t -> StrUtil.isEmpty(t.getSkipType()) || skipType.equals(t.getSkipType()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new FlowException(ExceptionCons.NULL_SKIP_TYPE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlowNodeMapper getMapper() {
|
||||
return SpringUtils.getBean(FlowNodeMapper.class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.service;
|
||||
import org.dromara.warm.flow.entity.*;
|
||||
|
||||
import org.dromara.warm.flow.entity.FlowSkip;
|
||||
import org.dromara.warm.flow.service.WarmServiceImpl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.dromara.warm.flow.mapper.FlowSkipMapper;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 节点跳转关联Service业务层处理
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
@Service
|
||||
public class SkipService extends WarmServiceImpl<FlowSkip> {
|
||||
public int deleteSkipByDefIds(Collection<? extends Serializable> defIds) {
|
||||
return getMapper().deleteSkipByDefIds(defIds);
|
||||
}
|
||||
|
||||
public List<FlowSkip> getByDefId(Long definitionId) {
|
||||
return list(new FlowSkip().setDefinitionId(definitionId));
|
||||
}
|
||||
|
||||
public List<FlowSkip> getByDefIdAndNowNodeCode(Long definitionId, String nodeCode) {
|
||||
return list(new FlowSkip().setDefinitionId(definitionId).setNowNodeCode(nodeCode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlowSkipMapper getMapper() {
|
||||
return SpringUtils.getBean(FlowSkipMapper.class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,993 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.service;
|
||||
import org.dromara.warm.flow.json.JsonUtil;
|
||||
|
||||
import org.dromara.warm.flow.entity.*;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.constant.ExceptionCons;
|
||||
import org.dromara.warm.flow.constant.FlowCons;
|
||||
import org.dromara.warm.flow.dto.*;
|
||||
import org.dromara.warm.flow.enums.*;
|
||||
import org.dromara.warm.flow.listener.ListenerVariable;
|
||||
import org.dromara.warm.flow.service.WarmServiceImpl;
|
||||
import org.dromara.warm.flow.utils.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
import org.dromara.warm.flow.entity.FlowTask;
|
||||
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.dromara.warm.flow.mapper.FlowTaskMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 待办任务Service业务层处理
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-29
|
||||
*/
|
||||
@Service
|
||||
public class TaskService extends WarmServiceImpl<FlowTask> {
|
||||
public FlowInstance pass(Long taskId, String message, Map<String, Object> variable) {
|
||||
return skip(taskId, new FlowParams(SkipType.PASS.getKey(), message, variable));
|
||||
}
|
||||
|
||||
public FlowInstance passAtWill(Long taskId, String nodeCode, String message, Map<String, Object> variable) {
|
||||
return skip(taskId, new FlowParams(nodeCode, SkipType.PASS.getKey(), message, variable));
|
||||
}
|
||||
|
||||
public FlowInstance pass(Long taskId, String message, Map<String, Object> variable, String flowStatus, String hisStatus) {
|
||||
return skip(taskId, new FlowParams(SkipType.PASS.getKey(), message, variable, flowStatus, hisStatus));
|
||||
}
|
||||
|
||||
public FlowInstance passAtWill(Long taskId, String nodeCode, String message, Map<String, Object> variable
|
||||
, String flowStatus, String hisStatus) {
|
||||
return skip(taskId, new FlowParams(nodeCode, SkipType.PASS.getKey(), message, variable, flowStatus, hisStatus));
|
||||
}
|
||||
|
||||
|
||||
public FlowInstance reject(Long taskId, String message, Map<String, Object> variable) {
|
||||
return skip(taskId, new FlowParams(SkipType.REJECT.getKey(), message, variable));
|
||||
}
|
||||
|
||||
public FlowInstance rejectAtWill(Long taskId, String nodeCode, String message, Map<String, Object> variable) {
|
||||
return skip(taskId, new FlowParams(nodeCode, SkipType.REJECT.getKey(), message, variable));
|
||||
}
|
||||
|
||||
public FlowInstance reject(Long taskId, String message, Map<String, Object> variable, String flowStatus, String hisStatus) {
|
||||
return skip(taskId, new FlowParams(SkipType.REJECT.getKey(), message, variable, flowStatus, hisStatus));
|
||||
}
|
||||
|
||||
public FlowInstance rejectAtWill(Long taskId, String nodeCode, String message, Map<String, Object> variable
|
||||
, String flowStatus, String hisStatus) {
|
||||
return skip(taskId, new FlowParams(nodeCode, SkipType.REJECT.getKey(), message, variable, flowStatus, hisStatus));
|
||||
}
|
||||
|
||||
|
||||
public FlowInstance skip(Long taskId, FlowParams flowParams) {
|
||||
// 获取待办任务
|
||||
FlowTask task = getById(taskId);
|
||||
return skip(flowParams, task);
|
||||
}
|
||||
|
||||
public FlowInstance skipByInsId(Long instanceId, FlowParams flowParams) {
|
||||
return skip(flowParams, getTask(instanceId));
|
||||
}
|
||||
|
||||
public FlowInstance rejectLastByInsId(Long instanceId, FlowParams flowParams) {
|
||||
return rejectLast(getTask(instanceId), flowParams);
|
||||
}
|
||||
|
||||
public FlowInstance rejectLast(Long taskId, FlowParams flowParams) {
|
||||
return rejectLast(getById(taskId), flowParams);
|
||||
}
|
||||
|
||||
public FlowInstance rejectLast(FlowTask task, FlowParams flowParams) {
|
||||
flowParams.skipType(SkipType.REJECT.getKey());
|
||||
AssertUtil.isNull(task, ExceptionCons.NOT_FOUNT_TASK);
|
||||
// 获取当前任务的前置任务
|
||||
List<FlowHisTask> hisTaskList = FlowEngine.hisTaskService().getByInsId(task.getInstanceId());
|
||||
// 获取hisTaskList中TargetNodeCod等于task.getNodeCode()的,并且id最大的
|
||||
FlowHisTask lastHisTask = hisTaskList.stream()
|
||||
.filter(hisTask -> StrUtil.isNotEmpty(hisTask.getTargetNodeCode()))
|
||||
.filter(hisTask -> SkipType.isPass(hisTask.getSkipType()))
|
||||
.filter(hisTask -> {
|
||||
String targetCode = hisTask.getTargetNodeCode();
|
||||
if (targetCode.contains(",")) {
|
||||
return Arrays.asList(targetCode.split(",")).contains(task.getNodeCode());
|
||||
} else {
|
||||
return targetCode.equals(task.getNodeCode());
|
||||
}
|
||||
})
|
||||
.max(Comparator.comparingLong(FlowHisTask::getId))
|
||||
.orElse(null);
|
||||
|
||||
AssertUtil.isNull(lastHisTask, ExceptionCons.NOT_FOUNT_LAST_TASK);
|
||||
flowParams.nodeCode(lastHisTask.getNodeCode());
|
||||
return skip(flowParams, task);
|
||||
}
|
||||
|
||||
public FlowInstance taskBackByInsId(Long instanceId, FlowParams flowParams) {
|
||||
// 获取当前任务的前置任务
|
||||
FlowHisTask lastHisTask = taskBack(flowParams, instanceId);
|
||||
List<FlowNode> suffixNodeList = FlowEngine.nodeService().suffixNodeList(lastHisTask.getDefinitionId()
|
||||
, lastHisTask.getNodeCode());
|
||||
|
||||
List<String> suffixNodeCodes = StreamUtils.toList(suffixNodeList, FlowNode::getNodeCode);
|
||||
List<FlowTask> taskList = FlowEngine.taskService().getByInsIdAndNodeCodes(instanceId, suffixNodeCodes);
|
||||
AssertUtil.isEmpty(taskList, ExceptionCons.NOT_FOUNT_HANDLED_TASK_HANDLER);
|
||||
return skip(flowParams, taskList.get(0));
|
||||
}
|
||||
|
||||
public FlowInstance taskBack(Long taskId, FlowParams flowParams) {
|
||||
FlowTask task = getById(taskId);
|
||||
AssertUtil.isNull(task, ExceptionCons.NOT_FOUNT_TASK);
|
||||
taskBack(flowParams, task.getInstanceId());
|
||||
return skip(flowParams, task);
|
||||
}
|
||||
|
||||
public FlowInstance skip(FlowParams flowParams, FlowTask task) {
|
||||
// TODO min 后续考虑并发问题,待办任务和实例表不同步,可给待办任务id加锁,抽取所接口,方便后续兼容分布式锁
|
||||
// 流程开启前正确性校验
|
||||
R r = getAndCheck(task);
|
||||
flowParams.variable(MapUtil.mergeAll(r.instance.getVariableMap(), flowParams.getVariable()));
|
||||
// 非第一个记得跳转类型必传
|
||||
if (!NodeType.isStart(task.getNodeType())) {
|
||||
AssertUtil.isFalse(StrUtil.isNotEmpty(flowParams.getSkipType()), ExceptionCons.NULL_CONDITION_VALUE);
|
||||
}
|
||||
task.setUserList(FlowEngine.userService().listByAssociatedAndTypes(task.getId()));
|
||||
FlowCombine flowCombine = FlowEngine.defService().getFlowCombineNoDef(r.definition.getId());
|
||||
|
||||
// 执行开始监听器
|
||||
ListenerUtil.executeStart(new ListenerVariable(r.definition, r.instance, r.nowNode, flowParams.getVariable()
|
||||
, task).setFlowParams(flowParams));
|
||||
|
||||
// 如果是受托人在处理任务,需要处理一条委派记录,并且更新委托人,回到计划审批人,然后直接返回流程实例
|
||||
if (!flowParams.isIgnoreDepute() && handleDepute(task, flowParams)) {
|
||||
return r.instance;
|
||||
}
|
||||
|
||||
// 判断当前处理人是否有权限处理
|
||||
checkAuth(task, flowParams);
|
||||
|
||||
//或签、会签、票签逻辑处理
|
||||
if (!flowParams.isIgnoreCooperate() && cooperate(r.nowNode, task, flowParams)) {
|
||||
return r.instance;
|
||||
}
|
||||
|
||||
// 获取后续任务节点结合
|
||||
PathWayData pathWayData = new PathWayData().setInsId(task.getInstanceId()).setSkipType(flowParams.getSkipType());
|
||||
FlowNode nextNode = FlowEngine.nodeService().getNextNode(r.nowNode, flowParams.getNodeCode()
|
||||
, flowParams.getSkipType(), pathWayData, flowCombine);
|
||||
List<FlowNode> nextNodes = FlowEngine.nodeService().getNextByCheckGateway(flowParams.getVariable()
|
||||
, nextNode, pathWayData, flowCombine);
|
||||
|
||||
// 判断并行网关和包容网关节点只剩一个前置代办任务,才能生成新的代办任务
|
||||
isGenerateNewTask(pathWayData, r.instance, nextNodes);
|
||||
pathWayData.getTargetNodes().addAll(nextNodes);
|
||||
|
||||
// 设置流程图元数据
|
||||
r.instance.setDefJson(FlowEngine.chartService().skipMetadata(pathWayData));
|
||||
|
||||
// 构建增待办任务和设置结束任务历史记录
|
||||
List<FlowTask> addTasks = StreamUtils.toList(nextNodes, node -> addTask(node, r.instance, r.definition, flowParams));
|
||||
|
||||
// 办理人变量替换
|
||||
ExpressionUtil.evalVariable(addTasks, flowParams.variable(MapUtil.mergeAll(r.instance.getVariableMap(), flowParams.getVariable())));
|
||||
|
||||
// 执行分派监听器
|
||||
ListenerUtil.executeAssignment(new ListenerVariable(r.definition, r.instance, r.nowNode, flowParams.getVariable()
|
||||
, task, nextNodes, addTasks).setFlowParams(flowParams));
|
||||
|
||||
// 更新流程信息
|
||||
updateFlowInfo(task, r.instance, addTasks, flowParams, nextNodes);
|
||||
|
||||
// 一票否决(谨慎使用),如果退回,退回指向节点后还存在其他正在执行的待办任务,转历史任务,状态都为失效,重走流程。
|
||||
if (CollUtil.isNotEmpty(nextNodes) && SkipType.isReject(flowParams.getSkipType())) {
|
||||
oneVoteVeto(task, nextNodes.get(0).getNodeCode(), flowCombine);
|
||||
}
|
||||
|
||||
// 处理未完成的任务,当流程完成,还存在待办任务未完成,转历史任务,状态完成。
|
||||
handUndoneTask(r.instance);
|
||||
|
||||
// 执行完成和创建监听器
|
||||
ListenerUtil.endCreateListener(new ListenerVariable(r.definition, r.instance, r.nowNode
|
||||
, flowParams.getVariable(), task, nextNodes, addTasks).setFlowParams(flowParams));
|
||||
|
||||
return r.instance;
|
||||
}
|
||||
|
||||
public FlowInstance revoke(Long instanceId, FlowParams flowParams) {
|
||||
flowParams.skipType(SkipType.REJECT.getKey());
|
||||
// 删除待办任务,保存历史,删除所有代办任务的权限人
|
||||
if (StrUtil.isEmpty(flowParams.getFlowStatus())) {
|
||||
flowParams.flowStatus(FlowStatus.CANCEL.getKey());
|
||||
}
|
||||
|
||||
FlowInstance instance = FlowEngine.insService().getById(instanceId);
|
||||
AssertUtil.isNull(instance, ExceptionCons.NOT_FOUNT_INSTANCE);
|
||||
FlowDefinition definition = FlowEngine.defService().getById(instance.getDefinitionId());
|
||||
AssertUtil.isFalse(judgeActivityStatus(definition, instance), ExceptionCons.NOT_ACTIVITY);
|
||||
AssertUtil.isTrue(NodeType.isEnd(instance.getNodeType()), ExceptionCons.FLOW_FINISH);
|
||||
flowParams.variable(MapUtil.mergeAll(instance.getVariableMap(), flowParams.getVariable()));
|
||||
|
||||
List<FlowTask> taskList = getByInsId(instanceId);
|
||||
FlowCombine flowCombine = FlowEngine.defService().getFlowCombine(definition);
|
||||
Map<String, FlowNode> nodeMap = StreamUtils.toMap(flowCombine.getAllNodes(), FlowNode::getNodeCode, node -> node);
|
||||
// 执行开始监听器
|
||||
taskList.forEach(task -> ListenerUtil.executeStart(new ListenerVariable(definition, instance
|
||||
, nodeMap.get(task.getNodeCode()), flowParams.getVariable(), task).setFlowParams(flowParams)));
|
||||
|
||||
// 验证权限是不是当前任务的发起人
|
||||
if (!flowParams.isIgnore()) {
|
||||
AssertUtil.isFalse(instance.getCreateBy().equals(flowParams.getHandler())
|
||||
, ExceptionCons.NOT_DEF_PROMOTER_NOT_CANCEL);
|
||||
}
|
||||
|
||||
// 获取开始节点
|
||||
FlowNode startNode = StreamUtils.findFirstValue(flowCombine.getAllNodes(), node -> NodeType.isStart(node.getNodeType()));
|
||||
// 获取下一个节点,如果是网关节点,则重新获取后续节点
|
||||
PathWayData pathWayData = new PathWayData().setInsId(instanceId).setSkipType(flowParams.getSkipType());
|
||||
FlowNode nextNode = FlowEngine.nodeService().getNextNode(startNode, null, SkipType.PASS.getKey()
|
||||
, null, flowCombine);
|
||||
List<FlowNode> nextNodes = FlowEngine.nodeService().getNextByCheckGateway(flowParams.getVariable(), nextNode
|
||||
, pathWayData, flowCombine);
|
||||
pathWayData.getTargetNodes().addAll(nextNodes);
|
||||
// 设置流程图元数据
|
||||
instance.setDefJson(FlowEngine.chartService().skipMetadata(pathWayData));
|
||||
|
||||
// 查询任务,如果前一个节点是并行网关,可能任务表有多个任务,增加查询和判断
|
||||
List<FlowTask> curTaskList = list(new FlowTask().setInstanceId(instance.getId()));
|
||||
AssertUtil.isEmpty(curTaskList, ExceptionCons.NOT_FOUND_FLOW_TASK);
|
||||
|
||||
// 给回退到的那个节点赋权限-给当前处理人权限
|
||||
List<FlowTask> addTasks = StreamUtils.toList(nextNodes, node -> addTask(node, instance, definition, flowParams));
|
||||
|
||||
// 办理人变量替换
|
||||
ExpressionUtil.evalVariable(addTasks, flowParams.variable(MapUtil.mergeAll(instance.getVariableMap(), flowParams.getVariable())));
|
||||
|
||||
// 执行分派监听器
|
||||
taskList.forEach(task -> ListenerUtil.executeAssignment(new ListenerVariable(definition, instance,
|
||||
nodeMap.get(task.getNodeCode()), flowParams.getVariable(), task, nextNodes, addTasks)
|
||||
.setFlowParams(flowParams)));
|
||||
|
||||
// 设置流程历史任务信息
|
||||
List<FlowHisTask> insHisList = FlowEngine.hisTaskService().setSkipHisList(curTaskList, nextNodes, flowParams);
|
||||
FlowEngine.hisTaskService().saveBatch(insHisList);
|
||||
// 待办任务和处理人
|
||||
removeAndUser(curTaskList);
|
||||
List<FlowUser> users = FlowEngine.userService().taskAddUsers(addTasks);
|
||||
|
||||
// 设置任务完成后的实例相关信息
|
||||
setInsFinishInfo(instance, addTasks, flowParams);
|
||||
if (CollUtil.isNotEmpty(addTasks)) {
|
||||
saveBatch(addTasks);
|
||||
}
|
||||
FlowEngine.insService().updateById(instance);
|
||||
// 保存下一个待办任务的权限人
|
||||
FlowEngine.userService().saveBatch(users);
|
||||
|
||||
// 执行完成和创建监听器
|
||||
taskList.forEach(task -> ListenerUtil.endCreateListener(new ListenerVariable(definition, instance,
|
||||
nodeMap.get(task.getNodeCode()), flowParams.getVariable(), task, nextNodes, addTasks).setFlowParams(flowParams)));
|
||||
return instance;
|
||||
}
|
||||
|
||||
public FlowInstance terminationByInsId(Long instanceId, FlowParams flowParams) {
|
||||
AssertUtil.isNull(instanceId, ExceptionCons.NULL_INSTANCE_ID);
|
||||
// 获取待办任务
|
||||
List<FlowTask> taskList = FlowEngine.taskService().getByInsId(instanceId);
|
||||
AssertUtil.isEmpty(taskList, ExceptionCons.NOT_FOUNT_TASK);
|
||||
FlowTask task = taskList.get(0);
|
||||
return termination(task, flowParams);
|
||||
}
|
||||
|
||||
public FlowInstance termination(Long taskId, FlowParams flowParams) {
|
||||
return termination(getById(taskId), flowParams);
|
||||
}
|
||||
|
||||
public FlowInstance termination(FlowTask task, FlowParams flowParams) {
|
||||
R r = getAndCheck(task);
|
||||
flowParams.skipType(SkipType.PASS.getKey());
|
||||
flowParams.variable(MapUtil.mergeAll(r.instance.getVariableMap(), flowParams.getVariable()));
|
||||
ListenerUtil.executeStart(new ListenerVariable(r.definition, r.instance, r.nowNode, flowParams.getVariable()
|
||||
, task).setFlowParams(flowParams));
|
||||
|
||||
// 判断当前处理人是否有权限处理
|
||||
task.setUserList(FlowEngine.userService().listByAssociatedAndTypes(task.getId()));
|
||||
checkAuth(task, flowParams);
|
||||
|
||||
// 所有待办转历史
|
||||
FlowNode endNode = FlowEngine.nodeService().getEndNode(r.instance.getDefinitionId());
|
||||
|
||||
// 设置流程图元数据
|
||||
PathWayData pathWayData = new PathWayData()
|
||||
.setInsId(task.getInstanceId())
|
||||
.setSkipType(flowParams.getSkipType())
|
||||
.setPathWayNodes(Collections.singletonList(r.nowNode))
|
||||
.setTargetNodes(Collections.singletonList(endNode));
|
||||
r.instance.setDefJson(FlowEngine.chartService().skipMetadata(pathWayData));
|
||||
|
||||
// 流程实例完成
|
||||
r.instance.setNodeType(endNode.getNodeType())
|
||||
.setNodeCode(endNode.getNodeCode())
|
||||
.setNodeName(endNode.getNodeName())
|
||||
.setFlowStatus(StrUtil.emptyToDefault(flowParams.getFlowStatus(), FlowStatus.TERMINATE.getKey()));
|
||||
|
||||
// 待办任务转历史
|
||||
flowParams.flowStatus(r.instance.getFlowStatus());
|
||||
FlowHisTask insHis = FlowEngine.hisTaskService().setSkipInsHis(task, Collections.singletonList(endNode)
|
||||
, flowParams);
|
||||
FlowEngine.hisTaskService().save(insHis);
|
||||
FlowEngine.insService().updateById(r.instance);
|
||||
|
||||
// 删除流程相关办理人
|
||||
FlowEngine.userService().deleteByTaskIds(Collections.singletonList(task.getId()));
|
||||
|
||||
// 处理未完成的任务,当流程完成,还存在待办任务未完成,转历史任务,状态完成。
|
||||
handUndoneTask(r.instance);
|
||||
// 最后判断是否存在节点监听器,存在执行节点监听器
|
||||
ListenerUtil.executeFinish(new ListenerVariable(r.definition, r.instance, r.nowNode, flowParams.getVariable()
|
||||
, task).setFlowParams(flowParams));
|
||||
return r.instance;
|
||||
}
|
||||
|
||||
public boolean deleteByInsIds(List<Long> instanceIds) {
|
||||
List<FlowInstance> instanceList = FlowEngine.insService().getByIds(instanceIds);
|
||||
FlowDefinition definition;
|
||||
for (FlowInstance instance : instanceList) {
|
||||
definition = FlowEngine.defService().getById(instance.getDefinitionId());
|
||||
AssertUtil.isFalse(judgeActivityStatus(definition, instance), ExceptionCons.NOT_ACTIVITY);
|
||||
}
|
||||
return SqlHelper.retBool(getMapper().deleteByInsIds(instanceIds));
|
||||
}
|
||||
|
||||
public boolean transfer(Long taskId, FlowParams flowParams) {
|
||||
AssertUtil.isNull(taskId, ExceptionCons.NULL_TASK_ID);
|
||||
AssertUtil.isNull(flowParams.getHandler(), ExceptionCons.HANDLER_NOT_EMPTY);
|
||||
AssertUtil.isNull(flowParams.getAddHandlers(), ExceptionCons.NULL_TRANSFER_HANDLER);
|
||||
List<FlowUser> users = FlowEngine.userService().getByProcessedBys(taskId, flowParams.getAddHandlers(), UserType.TRANSFER.getKey());
|
||||
AssertUtil.isNotEmpty(users, ExceptionCons.IS_ALREADY_TRANSFER);
|
||||
flowParams.cooperateType(CooperateType.TRANSFER.getKey())
|
||||
.reductionHandlers(Collections.singletonList(flowParams.getHandler()));
|
||||
|
||||
return updateHandler(taskId, flowParams);
|
||||
}
|
||||
|
||||
public boolean depute(Long taskId, FlowParams flowParams) {
|
||||
AssertUtil.isNull(taskId, ExceptionCons.NULL_TASK_ID);
|
||||
AssertUtil.isNull(flowParams.getHandler(), ExceptionCons.HANDLER_NOT_EMPTY);
|
||||
AssertUtil.isNull(flowParams.getAddHandlers(), ExceptionCons.NULL_DEPUTE_HANDLER);
|
||||
List<FlowUser> users = FlowEngine.userService().getByProcessedBys(taskId, flowParams.getAddHandlers(), UserType.DEPUTE.getKey());
|
||||
AssertUtil.isNotEmpty(users, ExceptionCons.IS_ALREADY_DEPUTE);
|
||||
flowParams.cooperateType(CooperateType.DEPUTE.getKey())
|
||||
.reductionHandlers(Collections.singletonList(flowParams.getHandler()));
|
||||
|
||||
return updateHandler(taskId, flowParams);
|
||||
}
|
||||
|
||||
public boolean addSignature(Long taskId, FlowParams flowParams) {
|
||||
AssertUtil.isNull(taskId, ExceptionCons.NULL_TASK_ID);
|
||||
AssertUtil.isNull(flowParams.getHandler(), ExceptionCons.HANDLER_NOT_EMPTY);
|
||||
AssertUtil.isNull(flowParams.getAddHandlers(), ExceptionCons.NULL_ADD_SIGNATURE_HANDLER);
|
||||
List<FlowUser> users = FlowEngine.userService().getByProcessedBys(taskId, flowParams.getAddHandlers(), UserType.APPROVAL.getKey());
|
||||
AssertUtil.isNotEmpty(users, ExceptionCons.IS_ALREADY_SIGN);
|
||||
flowParams.cooperateType(CooperateType.ADD_SIGNATURE.getKey());
|
||||
|
||||
return updateHandler(taskId, flowParams);
|
||||
}
|
||||
|
||||
public boolean reductionSignature(Long taskId, FlowParams flowParams) {
|
||||
AssertUtil.isNull(taskId, ExceptionCons.NULL_TASK_ID);
|
||||
AssertUtil.isNull(flowParams.getHandler(), ExceptionCons.HANDLER_NOT_EMPTY);
|
||||
AssertUtil.isNull(flowParams.getReductionHandlers(), ExceptionCons.NULL_REDUCTION_SIGNATURE_HANDLER);
|
||||
List<FlowUser> users = FlowEngine.userService().listByAssociatedAndTypes(taskId
|
||||
, UserType.APPROVAL.getKey(), UserType.TRANSFER.getKey());
|
||||
AssertUtil.isTrue(CollUtil.isEmpty(users) || users.size() == 1, ExceptionCons.REDUCTION_SIGN_ONE_ERROR);
|
||||
flowParams.cooperateType(CooperateType.REDUCTION_SIGNATURE.getKey());
|
||||
|
||||
return updateHandler(taskId, flowParams);
|
||||
}
|
||||
|
||||
public boolean updateHandler(Long taskId, FlowParams flowParams) {
|
||||
// 获取待办任务
|
||||
R r = getAndCheck(taskId);
|
||||
flowParams.variable(MapUtil.mergeAll(r.instance.getVariableMap(), flowParams.getVariable()));
|
||||
// 执行开始监听器
|
||||
ListenerUtil.executeStart(new ListenerVariable(r.definition, r.instance, r.nowNode, null, r.task));
|
||||
|
||||
// 获取给谁的权限
|
||||
if (!flowParams.isIgnore()) {
|
||||
// 判断当前处理人是否有权限,获取当前办理人的权限
|
||||
List<String> permissions = flowParams.getPermissionFlag();
|
||||
// 获取任务权限人
|
||||
List<String> taskPermissions = FlowEngine.userService().getPermission(taskId
|
||||
, UserType.APPROVAL.getKey(), UserType.TRANSFER.getKey(), UserType.DEPUTE.getKey());
|
||||
AssertUtil.isTrue(CollUtil.isNotEmpty(taskPermissions) && (CollUtil.isEmpty(permissions)
|
||||
|| !CollUtil.containsAny(permissions, taskPermissions)), ExceptionCons.NOT_AUTHORITY);
|
||||
}
|
||||
// 留存历史记录
|
||||
flowParams.skipType(SkipType.NONE.getKey());
|
||||
FlowHisTask hisTask = null;
|
||||
// 删除对应的操作人
|
||||
if (CollUtil.isNotEmpty(flowParams.getReductionHandlers())) {
|
||||
for (String reductionHandler : flowParams.getReductionHandlers()) {
|
||||
FlowEngine.userService().remove(new FlowUser().setAssociated(taskId)
|
||||
.setProcessedBy(reductionHandler));
|
||||
}
|
||||
hisTask = FlowEngine.hisTaskService().setCooperateHis(r.task, flowParams, flowParams.getReductionHandlers());
|
||||
}
|
||||
|
||||
// 新增权限人
|
||||
if (CollUtil.isNotEmpty(flowParams.getAddHandlers())) {
|
||||
String type;
|
||||
if (CooperateType.TRANSFER.getKey().equals(flowParams.getCooperateType())) {
|
||||
type = UserType.TRANSFER.getKey();
|
||||
} else if (CooperateType.DEPUTE.getKey().equals(flowParams.getCooperateType())) {
|
||||
type = UserType.DEPUTE.getKey();
|
||||
} else {
|
||||
type = UserType.APPROVAL.getKey();
|
||||
}
|
||||
FlowEngine.userService().saveBatch(StreamUtils.toList(flowParams.getAddHandlers(), permission ->
|
||||
FlowEngine.userService().structureUser(taskId, permission
|
||||
, type, flowParams.getHandler())));
|
||||
hisTask = FlowEngine.hisTaskService().setCooperateHis(r.task, flowParams, flowParams.getAddHandlers());
|
||||
}
|
||||
if (ObjectUtil.isNotNull(hisTask)) {
|
||||
FlowEngine.hisTaskService().save(hisTask);
|
||||
}
|
||||
// 最后判断是否存在节点监听器,存在执行节点监听器
|
||||
ListenerUtil.executeFinish(new ListenerVariable(r.definition, r.instance, r.nowNode, flowParams.getVariable()
|
||||
, r.task));
|
||||
return true;
|
||||
}
|
||||
|
||||
public FlowInstance pendingByInsId(Long instanceId, FlowParams flowParams) {
|
||||
return pending(getTask(instanceId), flowParams);
|
||||
}
|
||||
|
||||
public FlowInstance pending(Long taskId, FlowParams flowParams) {
|
||||
// 获取待办任务
|
||||
FlowTask task = getById(taskId);
|
||||
return pending(task, flowParams);
|
||||
}
|
||||
|
||||
public FlowInstance pending(FlowTask task, FlowParams flowParams) {
|
||||
// TODO min 后续考虑并发问题,待办任务和实例表不同步,可给待办任务id加锁,抽取所接口,方便后续兼容分布式锁
|
||||
// 流程开启前正确性校验
|
||||
R r = getAndCheck(task);
|
||||
flowParams.flowStatus(StrUtil.emptyToDefault(flowParams.getFlowStatus(), FlowStatus.PENDING.getKey()));
|
||||
// 执行开始监听器
|
||||
ListenerUtil.executeStart(new ListenerVariable(r.definition, r.instance, r.nowNode, flowParams.getVariable()
|
||||
, r.task).setFlowParams(flowParams));
|
||||
|
||||
// 判断当前处理人是否有权限处理
|
||||
checkAuth(r.task, flowParams);
|
||||
|
||||
// 设置流程历史任务信息
|
||||
FlowHisTask insHis = FlowEngine.hisTaskService().notSkip(r.task, flowParams);
|
||||
FlowEngine.hisTaskService().save(insHis);
|
||||
|
||||
FlowEngine.insService().updateById(r.instance.setFlowStatus(flowParams.getFlowStatus()));
|
||||
|
||||
// 执行任务完成监听器
|
||||
ListenerUtil.executeFinish(new ListenerVariable(r.definition, r.instance, r.nowNode
|
||||
, flowParams.getVariable(), r.task));
|
||||
|
||||
return r.instance;
|
||||
}
|
||||
|
||||
public FlowTask addTask(FlowNode node, FlowInstance instance, FlowDefinition definition, FlowParams flowParams) {
|
||||
FlowTask addTask = new FlowTask();
|
||||
Date now = new Date();
|
||||
FlowEngine.dataFillHandler().idFill(addTask);
|
||||
addTask.setDefinitionId(instance.getDefinitionId())
|
||||
.setInstanceId(instance.getId())
|
||||
.setNodeCode(node.getNodeCode())
|
||||
.setNodeName(node.getNodeName())
|
||||
.setNodeType(node.getNodeType())
|
||||
.setFlowStatus(StrUtil.emptyToDefault(flowParams.getFlowStatus(),
|
||||
setFlowStatus(node.getNodeType(), flowParams.getSkipType())))
|
||||
.setCreateTime(now)
|
||||
.setPermissionList(StrUtil.splitTrim(node.getPermissionFlag(), FlowCons.SPLIT_AT));
|
||||
|
||||
if (StrUtil.isNotEmpty(node.getFormCustom()) && StrUtil.isNotEmpty(node.getFormPath())) {
|
||||
// 节点有自定义表单则使用
|
||||
addTask.setFormCustom(node.getFormCustom()).setFormPath(node.getFormPath());
|
||||
} else {
|
||||
addTask.setFormCustom(definition.getFormCustom()).setFormPath(definition.getFormPath());
|
||||
}
|
||||
|
||||
return addTask;
|
||||
}
|
||||
|
||||
public List<FlowTask> getByInsId(Long instanceId) {
|
||||
return list(new FlowTask().setInstanceId(instanceId));
|
||||
}
|
||||
|
||||
public List<FlowTask> getByInsIdAndNodeCodes(Long instanceId, List<String> nodeCodes) {
|
||||
return getMapper().getByInsIdAndNodeCodes(instanceId, nodeCodes);
|
||||
}
|
||||
|
||||
public void setInsFinishInfo(FlowInstance instance, List<FlowTask> addTasks, FlowParams flowParams) {
|
||||
instance.setUpdateTime(new Date());
|
||||
// 合并流程变量到实例对象
|
||||
mergeVariable(instance, flowParams.getVariable());
|
||||
if (CollUtil.isNotEmpty(addTasks)) {
|
||||
// 终结节点任务不算待办任务,取其中最后一个作为实例最终信息
|
||||
List<FlowTask> endTasks = addTasks.stream()
|
||||
.filter(addTask -> NodeType.isEnd(addTask.getNodeType()))
|
||||
.collect(Collectors.toList());
|
||||
addTasks.removeAll(endTasks);
|
||||
FlowTask finallyTask = CollUtil.getLast(endTasks);
|
||||
if (finallyTask == null) {
|
||||
finallyTask = getNextTask(addTasks);
|
||||
}
|
||||
instance.setNodeType(finallyTask.getNodeType())
|
||||
.setNodeCode(finallyTask.getNodeCode())
|
||||
.setNodeName(finallyTask.getNodeName())
|
||||
.setFlowStatus(finallyTask.getFlowStatus());
|
||||
}
|
||||
}
|
||||
|
||||
public void mergeVariable(FlowInstance instance, Map<String, Object> variable) {
|
||||
if (MapUtil.isNotEmpty(variable)) {
|
||||
String variableStr = instance.getVariable();
|
||||
Map<String, Object> deserialize = JsonUtil.strToMap(variableStr);
|
||||
deserialize.putAll(variable);
|
||||
instance.setVariable(JsonUtil.objToStr(deserialize));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据流程实例id获取操作人最近的已办历史任务
|
||||
*
|
||||
* @param flowParams 包含流程相关参数的对象
|
||||
* @param instanceId 流程实例id
|
||||
* @return 最近的已办历史任务
|
||||
*/
|
||||
private FlowHisTask taskBack(FlowParams flowParams, Long instanceId) {
|
||||
flowParams.skipType(SkipType.REJECT.getKey())
|
||||
.ignore(true)
|
||||
.ignoreDepute(true)
|
||||
.ignoreCooperate(true)
|
||||
.flowStatus(StrUtil.emptyToDefault(flowParams.getFlowStatus(), FlowStatus.TASK_BACK.getKey()));
|
||||
// 获取当前任务的前置任务
|
||||
List<FlowHisTask> hisTaskList = FlowEngine.hisTaskService().getByInsId(instanceId);
|
||||
// 获取hisTaskList中TargetNodeCod等于task.getNodeCode()的,并且id最大的
|
||||
FlowHisTask lastHisTask = hisTaskList.stream()
|
||||
.filter(hisTask -> StrUtil.isNotEmpty(hisTask.getApprover()))
|
||||
.filter(hisTask -> SkipType.isPass(hisTask.getSkipType()))
|
||||
.filter(hisTask -> hisTask.getApprover().equals(flowParams.getHandler()))
|
||||
.max(Comparator.comparingLong(FlowHisTask::getId))
|
||||
.orElse(null);
|
||||
AssertUtil.isNull(lastHisTask, ExceptionCons.NOT_FOUNT_HANDLED_TASK);
|
||||
flowParams.nodeCode(lastHisTask.getNodeCode());
|
||||
return lastHisTask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待办任务
|
||||
*
|
||||
* @param instanceId 实例id
|
||||
* @return 待办任务
|
||||
*/
|
||||
private FlowTask getTask(Long instanceId) {
|
||||
List<FlowTask> taskList = getByInsId(instanceId);
|
||||
AssertUtil.isEmpty(taskList, ExceptionCons.NOT_FOUNT_TASK);
|
||||
AssertUtil.isTrue(taskList.size() > 1, ExceptionCons.TASK_NOT_ONE);
|
||||
return taskList.get(0);
|
||||
}
|
||||
|
||||
private String setFlowStatus(Integer nodeType, String skipType) {
|
||||
// 根据审批动作确定流程状态
|
||||
if (NodeType.isStart(nodeType)) {
|
||||
return FlowStatus.TOBESUBMIT.getKey();
|
||||
} else if (NodeType.isEnd(nodeType)) {
|
||||
return FlowStatus.FINISHED.getKey();
|
||||
} else if (SkipType.isReject(skipType)) {
|
||||
return FlowStatus.REJECT.getKey();
|
||||
} else {
|
||||
return FlowStatus.APPROVAL.getKey();
|
||||
}
|
||||
}
|
||||
|
||||
private FlowTask getNextTask(List<FlowTask> tasks) {
|
||||
if (tasks.size() == 1) {
|
||||
return tasks.get(0);
|
||||
}
|
||||
return tasks.stream().max(Comparator.comparingLong(FlowTask::getId)).orElse(null);
|
||||
}
|
||||
|
||||
private void removeAndUser(List<FlowTask> taskList) {
|
||||
removeByIds(StreamUtils.toList(taskList, FlowTask::getId));
|
||||
FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(taskList, FlowTask::getId));
|
||||
}
|
||||
|
||||
private R getAndCheck(Long taskId) {
|
||||
AssertUtil.isNull(taskId, ExceptionCons.NULL_TASK_ID);
|
||||
return getAndCheck(getById(taskId));
|
||||
}
|
||||
|
||||
private R getAndCheck(FlowTask task) {
|
||||
AssertUtil.isNull(task, ExceptionCons.NOT_FOUNT_TASK);
|
||||
FlowInstance instance = FlowEngine.insService().getById(task.getInstanceId());
|
||||
AssertUtil.isNull(instance, ExceptionCons.NOT_FOUNT_INSTANCE);
|
||||
FlowDefinition definition = FlowEngine.defService().getById(instance.getDefinitionId());
|
||||
AssertUtil.isFalse(judgeActivityStatus(definition, instance), ExceptionCons.NOT_ACTIVITY);
|
||||
AssertUtil.isTrue(NodeType.isEnd(instance.getNodeType()), ExceptionCons.FLOW_FINISH);
|
||||
FlowNode nowNode = FlowEngine.nodeService().getByDefIdAndNodeCode(task.getDefinitionId(), task.getNodeCode());
|
||||
AssertUtil.isNull(nowNode, ExceptionCons.LOST_CUR_NODE);
|
||||
return new R(instance, definition, nowNode, task);
|
||||
}
|
||||
|
||||
private static class R {
|
||||
public final FlowInstance instance;
|
||||
public final FlowDefinition definition;
|
||||
public final FlowNode nowNode;
|
||||
public final FlowTask task;
|
||||
|
||||
public R(FlowInstance instance, FlowDefinition definition, FlowNode nowNode, FlowTask task) {
|
||||
this.instance = instance;
|
||||
this.definition = definition;
|
||||
this.nowNode = nowNode;
|
||||
this.task = task;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleDepute(FlowTask task, FlowParams flowParams) {
|
||||
// 获取受托人
|
||||
List<FlowUser> entrustedUserList = StreamUtils.filter(task.getUserList(),
|
||||
user -> UserType.DEPUTE.getKey().equals(user.getType())
|
||||
&& Objects.equals(flowParams.getHandler(), user.getProcessedBy()));
|
||||
if (CollUtil.isEmpty(entrustedUserList)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 记录受托人处理任务记录
|
||||
FlowUser entrustedUser = entrustedUserList.get(0);
|
||||
FlowHisTask hisTask = FlowEngine.hisTaskService().setDeputeHisTask(task, flowParams, entrustedUser);
|
||||
FlowEngine.hisTaskService().save(hisTask);
|
||||
FlowEngine.userService().removeById(entrustedUser.getId());
|
||||
|
||||
// 查询委托人,如果在flow_user不存在,则给委托人新增待办记录
|
||||
FlowUser deputeUser = FlowEngine.userService().getOne(new FlowUser().setAssociated(task.getId())
|
||||
.setProcessedBy(entrustedUser.getCreateBy()).setType(UserType.APPROVAL.getKey()));
|
||||
if (ObjectUtil.isNull(deputeUser)) {
|
||||
FlowUser newUser = FlowEngine.userService().structureUser(entrustedUser.getAssociated()
|
||||
, entrustedUser.getCreateBy()
|
||||
, UserType.APPROVAL.getKey(), entrustedUser.getProcessedBy());
|
||||
FlowEngine.userService().save(newUser);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 会签,票签,协作处理,返回true;或签或者会签、票签结束返回false
|
||||
*
|
||||
* @param nowNode 当前节点
|
||||
* @param task 任务
|
||||
* @param flowParams 流程参数
|
||||
* @return boolean
|
||||
*/
|
||||
private boolean cooperate(FlowNode nowNode, FlowTask task, FlowParams flowParams) {
|
||||
if (flowParams.isIgnore()) {
|
||||
return false;
|
||||
}
|
||||
String nodeRatio = nowNode.getNodeRatio();
|
||||
// 或签,直接返回
|
||||
if (CooperateType.isOrSign(nodeRatio)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 办理人和转办人列表
|
||||
List<FlowUser> todoList = FlowEngine.userService().listByAssociatedAndTypes(task.getId()
|
||||
, UserType.APPROVAL.getKey(), UserType.TRANSFER.getKey(), UserType.DEPUTE.getKey());
|
||||
|
||||
// 判断办理人是否有办理权限
|
||||
AssertUtil.isEmpty(flowParams.getHandler(), ExceptionCons.SIGN_NULL_HANDLER);
|
||||
FlowUser todoUser = CollUtil.getFirst(StreamUtils.filter(todoList, u -> Objects.equals(u.getProcessedBy(), flowParams.getHandler())));
|
||||
AssertUtil.isNull(todoUser, ExceptionCons.NOT_AUTHORITY);
|
||||
|
||||
// 除当前办理人外剩余办理人列表
|
||||
List<FlowUser> restList = StreamUtils.filter(todoList, u -> !Objects.equals(u.getProcessedBy(), flowParams.getHandler()));
|
||||
|
||||
// 会签并且当前人退回直接返回
|
||||
if (CooperateType.isCountersign(nodeRatio) && SkipType.isReject(flowParams.getSkipType())) {
|
||||
return removeRestList(restList);
|
||||
}
|
||||
|
||||
// 查询会签票签已办列表
|
||||
List<FlowHisTask> doneList = FlowEngine.hisTaskService().listByTaskId(task.getId());
|
||||
doneList = CollUtil.isEmpty(doneList) ? new ArrayList<>() : doneList;
|
||||
|
||||
// 总人数
|
||||
int allNum = todoList.size() + doneList.size();
|
||||
|
||||
// 通过历史记录
|
||||
List<FlowHisTask> donePassList = StreamUtils.filter(doneList
|
||||
, hisTask -> Objects.equals(hisTask.getSkipType(), SkipType.PASS.getKey()));
|
||||
|
||||
// 驳回历史记录
|
||||
List<FlowHisTask> doneRejectList = StreamUtils.filter(doneList
|
||||
, hisTask -> Objects.equals(hisTask.getSkipType(), SkipType.REJECT.getKey()));
|
||||
|
||||
boolean isPass = SkipType.isPass(flowParams.getSkipType());
|
||||
// 如果是票签默认或者spel表达式策略,则执行表达式
|
||||
if (CooperateType.isVoteSignDefault(nodeRatio) || CooperateType.isVoteSignRejectSpel(nodeRatio)) {
|
||||
Map<String, Object> variable = MapUtil.clone(flowParams.getVariable());
|
||||
variable.put("skipType", flowParams.getSkipType());
|
||||
variable.put("passNum", donePassList.size());
|
||||
variable.put("rejectNum", doneRejectList.size());
|
||||
variable.put("todoNum", todoList.size());
|
||||
variable.put("allNum", allNum);
|
||||
variable.put("passList", donePassList);
|
||||
variable.put("rejectList", doneRejectList);
|
||||
variable.put("todoList", todoList);
|
||||
if (ExpressionUtil.evalVoteSign(nodeRatio, variable)) {
|
||||
return removeRestList(restList);
|
||||
}
|
||||
} else {
|
||||
// 计算通过率
|
||||
BigDecimal passRatio = (isPass ? BigDecimal.ONE : BigDecimal.ZERO)
|
||||
.add(BigDecimal.valueOf(donePassList.size()))
|
||||
.divide(BigDecimal.valueOf(allNum), 4, RoundingMode.HALF_UP).multiply(MathUtil.ONE_HUNDRED);
|
||||
// 计算驳回率
|
||||
BigDecimal rejectRatio = (isPass ? BigDecimal.ZERO : BigDecimal.ONE)
|
||||
.add(BigDecimal.valueOf(doneRejectList.size()))
|
||||
.divide(BigDecimal.valueOf(allNum), 4, RoundingMode.HALF_UP).multiply(MathUtil.ONE_HUNDRED);
|
||||
|
||||
// 判断是否是票签中的固定通过人数,如果是则判断是否达到该人数
|
||||
if (CooperateType.isVoteSignPassCount(nodeRatio)) {
|
||||
String passCount = StrUtil.subSuf(nodeRatio, nodeRatio.indexOf("=") + 1);
|
||||
if ((isPass && donePassList.size() + 1 >= Integer.parseInt(passCount))
|
||||
|| (!isPass && doneRejectList.size() + 1 > allNum - Integer.parseInt(passCount))) {
|
||||
return removeRestList(restList);
|
||||
}
|
||||
} else if (CooperateType.isVoteSignRejectCount(nodeRatio)) {
|
||||
// 判断是否是票签中的固定驳回人数,如果是则判断是否达到该人数
|
||||
String rejectCount = StrUtil.subSuf(nodeRatio, nodeRatio.indexOf("=") + 1);
|
||||
if ((!isPass && doneRejectList.size() + 1 >= Integer.parseInt(rejectCount))
|
||||
|| (isPass && donePassList.size() + 1 > allNum - Integer.parseInt(rejectCount))) {
|
||||
return removeRestList(restList);
|
||||
}
|
||||
} else if ((!isPass && rejectRatio.compareTo(MathUtil.ONE_HUNDRED.subtract(new BigDecimal(nodeRatio))) > 0)
|
||||
|| (isPass && passRatio.compareTo(new BigDecimal(nodeRatio)) >= 0)) {
|
||||
// 提前不满足通过率或者满足通过率,删除剩余办理人,流程正常流程流转
|
||||
return removeRestList(restList);
|
||||
}
|
||||
}
|
||||
|
||||
// 当只剩一位待办用户时,由当前用户决定走向
|
||||
if (todoList.size() == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 添加历史任务
|
||||
FlowHisTask hisTask = FlowEngine.hisTaskService().setSignHisTask(task, flowParams, nodeRatio, isPass);
|
||||
FlowEngine.hisTaskService().save(hisTask);
|
||||
|
||||
// 删掉待办用户
|
||||
FlowEngine.userService().removeById(todoUser.getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除剩余办理人
|
||||
* @param restList 待办用户列表
|
||||
* @return boolean
|
||||
*/
|
||||
private static boolean removeRestList(List<FlowUser> restList) {
|
||||
if (CollUtil.isNotEmpty(restList)) {
|
||||
FlowEngine.userService().removeByIds(StreamUtils.toList(restList, FlowUser::getId));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断并行网关和包容网关节点只剩一个前置代办任务,才能生成新的代办任务
|
||||
*
|
||||
* @param pathWayData 办理过程中途径数据
|
||||
* @param instance 实例
|
||||
*/
|
||||
private void isGenerateNewTask(PathWayData pathWayData, FlowInstance instance, List<FlowNode> nextNodes) {
|
||||
if (SkipType.isReject(pathWayData.getSkipType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
DefJson defJson = JsonUtil.strToBean(instance.getDefJson(), DefJson.class);
|
||||
Map<String, NodeJson> nodeJsonMap = StreamUtils.toMap(defJson.getNodeList(), NodeJson::getNodeCode, node -> node);
|
||||
// 途径节点中的并行/包容网关,只剩一个前置待办任务时才能生成新的代办任务
|
||||
List<FlowNode> parallelOrInclusiveList = pathWayData.getPathWayNodes().stream()
|
||||
.filter(t -> NodeType.isGateWayParallel(t.getNodeType()) || NodeType.isGateWayInclusive(t.getNodeType()))
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isEmpty(parallelOrInclusiveList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<FlowNode> previousNodeList = FlowEngine.nodeService().previousNodeList(instance.getDefinitionId()
|
||||
, parallelOrInclusiveList.get(parallelOrInclusiveList.size() - 1).getNodeCode());
|
||||
// 前置节点中处于待办状态的数量
|
||||
long toDoCount = previousNodeList.stream()
|
||||
.map(FlowNode::getNodeCode)
|
||||
.map(nodeJsonMap::get)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(nodeJson -> ChartStatus.isToDo(nodeJson.getStatus()))
|
||||
.count();
|
||||
// 并行网关和包容网关还有多个前置待办任务,不可生成新的代办任务
|
||||
if (toDoCount <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
nextNodes.clear();
|
||||
String gatewayCode = parallelOrInclusiveList.get(0).getNodeCode();
|
||||
// 途径节点保留首个网关自身,其后的全部移除
|
||||
List<FlowNode> pathWayNodes = pathWayData.getPathWayNodes();
|
||||
for (int i = 0; i < pathWayNodes.size(); i++) {
|
||||
if (pathWayNodes.get(i).getNodeCode().equals(gatewayCode)) {
|
||||
pathWayNodes.subList(i + 1, pathWayNodes.size()).clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 途径连线从首个网关出发的(含)全部移除
|
||||
List<FlowSkip> pathWaySkips = pathWayData.getPathWaySkips();
|
||||
for (int i = 0; i < pathWaySkips.size(); i++) {
|
||||
if (pathWaySkips.get(i).getNowNodeCode().equals(gatewayCode)) {
|
||||
pathWaySkips.subList(i, pathWaySkips.size()).clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前处理人是否有权限处理
|
||||
*
|
||||
* @param task 当前任务(任务id)
|
||||
* @param flowParams:包含流程相关参数的对象
|
||||
*/
|
||||
private void checkAuth(FlowTask task, FlowParams flowParams) {
|
||||
if (flowParams.isIgnore()) {
|
||||
return;
|
||||
}
|
||||
// 查询审批人和转办人
|
||||
List<String> permissions = StreamUtils.toList(task.getUserList(), FlowUser::getProcessedBy);
|
||||
// 当前办理人拥有的权限和设计时候填的权限集合是否有交集,有说明有权限办理
|
||||
AssertUtil.isTrue(CollUtil.isNotEmpty(permissions) && (CollUtil.isEmpty(flowParams.getPermissionFlag())
|
||||
|| !CollUtil.containsAny(flowParams.getPermissionFlag(), permissions)), ExceptionCons.NULL_ROLE_NODE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 一票否决(谨慎使用),如果退回,退回指向节点后还存在其他正在执行的待办任务,转历史任务,状态都为退回,重走流程。
|
||||
*
|
||||
* @param task 当前任务
|
||||
* @param nextNodeCode 下一个节点编码
|
||||
* @param flowCombine 流程数据集合
|
||||
*/
|
||||
private void oneVoteVeto(FlowTask task, String nextNodeCode, FlowCombine flowCombine) {
|
||||
// 一票否决(谨慎使用),如果退回,退回指向节点后还存在其他正在执行的待办任务,转历史任务,状态失效,重走流程。
|
||||
List<FlowTask> tasks = list(new FlowTask().setInstanceId(task.getInstanceId()));
|
||||
// 属于退回指向节点的后置未完成的任务
|
||||
List<FlowTask> noDoneTasks = new ArrayList<>();
|
||||
List<FlowNode> suffixNodeList = FlowEngine.nodeService().suffixNodeList(nextNodeCode, flowCombine);
|
||||
List<String> suffixCodes = StreamUtils.toList(suffixNodeList, FlowNode::getNodeCode);
|
||||
for (FlowTask flowTask : tasks) {
|
||||
if (suffixCodes.contains(flowTask.getNodeCode())) {
|
||||
noDoneTasks.add(flowTask);
|
||||
}
|
||||
}
|
||||
if (CollUtil.isNotEmpty(noDoneTasks)) {
|
||||
removeAndUser(noDoneTasks);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理未完成的任务,当流程完成,还存在待办任务未完成,转历史任务,状态完成。
|
||||
*
|
||||
* @param instance 流程实例
|
||||
*/
|
||||
private void handUndoneTask(FlowInstance instance) {
|
||||
if (NodeType.isEnd(instance.getNodeType())) {
|
||||
List<FlowTask> taskList = list(new FlowTask().setInstanceId(instance.getId()));
|
||||
if (CollUtil.isNotEmpty(taskList)) {
|
||||
removeAndUser(taskList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新流程信息
|
||||
*
|
||||
* @param task 当前任务
|
||||
* @param instance 流程实例
|
||||
* @param addTasks 新增待办任务
|
||||
* @param flowParams 包含流程相关参数的对象
|
||||
* @param nextNodes 下一个节点集合
|
||||
*/
|
||||
private void updateFlowInfo(FlowTask task, FlowInstance instance, List<FlowTask> addTasks, FlowParams flowParams
|
||||
, List<FlowNode> nextNodes) {
|
||||
// 设置流程历史任务信息
|
||||
FlowHisTask insHis = FlowEngine.hisTaskService().setSkipInsHis(task, nextNodes, flowParams);
|
||||
FlowEngine.hisTaskService().save(insHis);
|
||||
removeAndUser(Collections.singletonList(task));
|
||||
// 待办任务设置处理人
|
||||
List<FlowUser> users = FlowEngine.userService().taskAddUsers(addTasks);
|
||||
|
||||
// 设置任务完成后的实例相关信息
|
||||
setInsFinishInfo(instance, addTasks, flowParams);
|
||||
if (CollUtil.isNotEmpty(addTasks)) {
|
||||
saveBatch(addTasks);
|
||||
}
|
||||
FlowEngine.insService().updateById(instance);
|
||||
// 保存下一个待办任务的权限人
|
||||
FlowEngine.userService().saveBatch(users);
|
||||
}
|
||||
|
||||
private boolean judgeActivityStatus(FlowDefinition definition, FlowInstance instance) {
|
||||
return ActivityStatus.isActivity(definition.getActivityStatus())
|
||||
&& ActivityStatus.isActivity(instance.getActivityStatus());
|
||||
}
|
||||
|
||||
|
||||
public FlowDto load(Long taskId, FlowParams flowParams) {
|
||||
R r = getAndCheck(taskId);
|
||||
|
||||
FlowDto flowDto = new FlowDto();
|
||||
flowDto.setData(r.instance.getVariableMap().get(FlowCons.FORM_DATA));
|
||||
|
||||
return flowDto;
|
||||
}
|
||||
|
||||
public FlowDto hisLoad(Long hisTaskId, FlowParams flowParams) {
|
||||
FlowHisTask hisTask = FlowEngine.hisTaskService().getById(hisTaskId);
|
||||
AssertUtil.isNull(hisTask, ExceptionCons.NOT_FOUND_FLOW_TASK);
|
||||
|
||||
FlowDefinition definition = FlowEngine.defService().getById(hisTask.getDefinitionId());
|
||||
AssertUtil.isNull(definition, ExceptionCons.NOT_FOUNT_DEF);
|
||||
|
||||
FlowNode nowNode = CollUtil.getFirst(FlowEngine.nodeService()
|
||||
.getMapper().getByNodeCodes(Collections.singletonList(hisTask.getNodeCode()), hisTask.getDefinitionId()));
|
||||
AssertUtil.isNull(nowNode, ExceptionCons.LOST_CUR_NODE);
|
||||
|
||||
FlowDto flowDto = new FlowDto();
|
||||
flowDto.setData(hisTask.getVariableMap().get(FlowCons.FORM_DATA));
|
||||
|
||||
return flowDto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlowTaskMapper getMapper() {
|
||||
return SpringUtils.getBean(FlowTaskMapper.class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.service;
|
||||
import org.dromara.warm.flow.entity.*;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.entity.FlowTask;
|
||||
import org.dromara.warm.flow.entity.FlowUser;
|
||||
import org.dromara.warm.flow.enums.UserType;
|
||||
import org.dromara.warm.flow.service.WarmServiceImpl;
|
||||
|
||||
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.dromara.warm.flow.mapper.FlowUserMapper;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 流程用户Service业务层处理
|
||||
*
|
||||
* @author xiarg
|
||||
* @since 2024/5/10 13:57
|
||||
*/
|
||||
@Service
|
||||
public class UserService extends WarmServiceImpl<FlowUser> {
|
||||
public List<FlowUser> taskAddUsers(List<FlowTask> addTasks) {
|
||||
List<FlowUser> taskUserList = new ArrayList<>();
|
||||
if (CollUtil.isNotEmpty(addTasks)) {
|
||||
StreamUtils.toList(addTasks, task -> taskUserList.addAll(taskAddUser(task)));
|
||||
}
|
||||
return taskUserList;
|
||||
}
|
||||
|
||||
public List<FlowUser> taskAddUser(FlowTask task) {
|
||||
// 遍历权限集合,生成流程节点的权限
|
||||
List<FlowUser> userList = StreamUtils.toList(task.getPermissionList()
|
||||
, permission -> structureUser(task.getId(), permission, UserType.APPROVAL.getKey()));
|
||||
task.setUserList(userList);
|
||||
return userList;
|
||||
}
|
||||
|
||||
public void deleteByTaskIds(List<Long> ids) {
|
||||
getMapper().deleteByTaskIds(ids);
|
||||
}
|
||||
|
||||
public List<String> getPermission(Long associated, String... types) {
|
||||
if (ArrayUtil.isEmpty(types)) {
|
||||
return StreamUtils.toList(list(new FlowUser().setAssociated(associated)), FlowUser::getProcessedBy);
|
||||
}
|
||||
if (types.length == 1) {
|
||||
return StreamUtils.toList(list(new FlowUser().setAssociated(associated).setType(types[0]))
|
||||
, FlowUser::getProcessedBy);
|
||||
}
|
||||
return StreamUtils.toList(getMapper().listByAssociatedAndTypes(Collections.singletonList(associated), types)
|
||||
, FlowUser::getProcessedBy);
|
||||
}
|
||||
|
||||
public List<FlowUser> listByAssociatedAndTypes(Long associated, String... types) {
|
||||
if (ArrayUtil.isEmpty(types)) {
|
||||
return list(new FlowUser().setAssociated(associated));
|
||||
}
|
||||
if (types.length == 1) {
|
||||
return list(new FlowUser().setAssociated(associated).setType(types[0]));
|
||||
}
|
||||
return getMapper().listByAssociatedAndTypes(Collections.singletonList(associated), types);
|
||||
}
|
||||
|
||||
public List<FlowUser> getByAssociateds(List<Long> associateds, String... types) {
|
||||
if (CollUtil.isNotEmpty(associateds) && associateds.size() == 1) {
|
||||
return listByAssociatedAndTypes(associateds.get(0), types);
|
||||
}
|
||||
return getMapper().listByAssociatedAndTypes(associateds, types);
|
||||
}
|
||||
|
||||
public List<FlowUser> listByProcessedBys(Long associated, String processedBy, String... types) {
|
||||
if (ArrayUtil.isEmpty(types)) {
|
||||
return list(new FlowUser().setAssociated(associated).setProcessedBy(processedBy));
|
||||
}
|
||||
if (types.length == 1) {
|
||||
return list(new FlowUser().setAssociated(associated).setProcessedBy(processedBy).setType(types[0]));
|
||||
}
|
||||
return getMapper().listByProcessedBys(associated, Collections.singletonList(processedBy), types);
|
||||
}
|
||||
|
||||
public List<FlowUser> getByProcessedBys(Long associated, List<String> processedBys, String... types) {
|
||||
if (CollUtil.isNotEmpty(processedBys) && processedBys.size() == 1) {
|
||||
return listByProcessedBys(associated, processedBys.get(0), types);
|
||||
}
|
||||
return getMapper().listByProcessedBys(associated, processedBys, types);
|
||||
}
|
||||
|
||||
|
||||
public boolean updatePermission(Long associated, List<String> permissions, String type, boolean clear,
|
||||
String handler) {
|
||||
// 判断是否clear,如果是true,则先删除当前关联id用户数据
|
||||
if (clear) {
|
||||
getMapper().delete(new FlowUser().setAssociated(associated).setCreateBy(handler));
|
||||
}
|
||||
// 再新增权限人
|
||||
saveBatch(StreamUtils.toList(permissions, permission -> structureUser(associated, permission, type, handler)));
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<FlowUser> structureUser(Long associated, List<String> permissionList, String type) {
|
||||
return StreamUtils.toList(permissionList, permission -> structureUser(associated, permission, type, null));
|
||||
}
|
||||
|
||||
public FlowUser structureUser(Long associated, String permission, String type) {
|
||||
return structureUser(associated, permission, type, null);
|
||||
}
|
||||
|
||||
public List<FlowUser> structureUser(Long associated, List<String> permissionList, String type, String handler) {
|
||||
return StreamUtils.toList(permissionList, permission -> structureUser(associated, permission, type, handler));
|
||||
}
|
||||
|
||||
public FlowUser structureUser(Long associated, String permission, String type, String handler) {
|
||||
Date now = new Date();
|
||||
FlowUser user = new FlowUser()
|
||||
.setType(type)
|
||||
.setProcessedBy(permission)
|
||||
.setAssociated(associated)
|
||||
.setCreateBy(handler);
|
||||
FlowEngine.dataFillHandler().idFill(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public FlowUserMapper getMapper() {
|
||||
return SpringUtils.getBean(FlowUserMapper.class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.entity.RootEntity;
|
||||
import org.dromara.warm.flow.handler.DataFillHandler;
|
||||
import org.dromara.warm.flow.mapper.WarmMapper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BaseService层处理,直接持有 Mapper,无独立 Dao 层
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023-03-17
|
||||
*/
|
||||
public abstract class WarmServiceImpl<T extends RootEntity> implements IWarmService<T> {
|
||||
|
||||
/**
|
||||
* 获取Mapper(由子类提供,Mapper 由 MyBatis 扫描 mapper 包注册)
|
||||
*/
|
||||
public abstract WarmMapper<T> getMapper();
|
||||
|
||||
@Override
|
||||
public T getById(Serializable id) {
|
||||
return getMapper().selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getByIds(Collection<? extends Serializable> ids) {
|
||||
return getMapper().selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> list(T entity) {
|
||||
return getMapper().selectList(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getOne(T entity) {
|
||||
List<T> list = getMapper().selectList(entity);
|
||||
return CollUtil.getFirst(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean exists(T entity) {
|
||||
return getMapper().selectCount(entity) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean save(T entity) {
|
||||
insertFill(entity);
|
||||
return SqlHelper.retBool(getMapper().insert(entity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateById(T entity) {
|
||||
updateFill(entity);
|
||||
return SqlHelper.retBool(getMapper().update(entity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeById(Serializable id) {
|
||||
return SqlHelper.retBool(getMapper().deleteById(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(T entity) {
|
||||
return SqlHelper.retBool(getMapper().delete(entity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeByIds(Collection<? extends Serializable> ids) {
|
||||
return SqlHelper.retBool(getMapper().deleteBatchIds(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveBatch(List<T> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return;
|
||||
}
|
||||
list.forEach(this::save);
|
||||
}
|
||||
|
||||
public void insertFill(T entity) {
|
||||
DataFillHandler dataFillHandler = FlowEngine.dataFillHandler();
|
||||
if (dataFillHandler == null) {
|
||||
return;
|
||||
}
|
||||
dataFillHandler.idFill(entity);
|
||||
dataFillHandler.insertFill(entity);
|
||||
}
|
||||
|
||||
public void updateFill(T entity) {
|
||||
DataFillHandler dataFillHandler = FlowEngine.dataFillHandler();
|
||||
if (dataFillHandler == null) {
|
||||
return;
|
||||
}
|
||||
dataFillHandler.updateFill(entity);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.strategy;
|
||||
|
||||
import org.dromara.warm.flow.constant.FlowCons;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 条件表达式接口
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public interface ConditionStrategy extends ExpressionStrategy<Boolean> {
|
||||
|
||||
/**
|
||||
* 条件表达式策略实现类集合
|
||||
*/
|
||||
List<ExpressionStrategy<Boolean>> EXPRESSION_STRATEGY_LIST = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
default void setExpression(ExpressionStrategy<Boolean> expressionStrategy) {
|
||||
EXPRESSION_STRATEGY_LIST.add(expressionStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
default String interceptStr() {
|
||||
return FlowCons.SPLIT_AT;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.strategy;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 表达式策略类接口
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public interface ExpressionStrategy<T> {
|
||||
|
||||
/**
|
||||
* 获取策略类型
|
||||
*
|
||||
* @return 类型
|
||||
*/
|
||||
String getType();
|
||||
|
||||
/**
|
||||
* 当选择截取,并且希望拼接上某些字符串,在进行截取
|
||||
*
|
||||
* @return 类型
|
||||
*/
|
||||
default String interceptStr() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行表达式
|
||||
*
|
||||
* @param expression 表达式
|
||||
* @param variable 流程变量
|
||||
* @return 执行结果
|
||||
*/
|
||||
T eval(String expression, Map<String, Object> variable);
|
||||
|
||||
|
||||
/**
|
||||
* 设置表达式
|
||||
*
|
||||
* @param expressionStrategy 表达式
|
||||
*/
|
||||
void setExpression(ExpressionStrategy<T> expressionStrategy);
|
||||
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.strategy;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 办理人表达式策略接口
|
||||
*
|
||||
* @author warm,battcn
|
||||
*/
|
||||
public interface HandlerStrategy extends ExpressionStrategy<List<String>> {
|
||||
|
||||
/**
|
||||
* 办理人表达式策略实现类集合
|
||||
*/
|
||||
List<ExpressionStrategy<List<String>>> EXPRESSION_STRATEGY_LIST = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
default void setExpression(ExpressionStrategy<List<String>> expressionStrategy) {
|
||||
EXPRESSION_STRATEGY_LIST.add(expressionStrategy);
|
||||
}
|
||||
|
||||
Object preEval(String expression, Map<String, Object> variable);
|
||||
|
||||
@Override
|
||||
default List<String> eval(String expression, Map<String, Object> variable) {
|
||||
return afterEval(preEval(expression, variable));
|
||||
}
|
||||
|
||||
default List<String> afterEval(Object o) {
|
||||
if (ObjectUtil.isNull(o)) {
|
||||
return null;
|
||||
}
|
||||
if (o instanceof List) {
|
||||
return StreamUtils.toList((List<?>) o, Object::toString);
|
||||
}
|
||||
if (o instanceof Object[]) {
|
||||
return Arrays.stream((Object[]) o).map(Object::toString).collect(Collectors.toList());
|
||||
}
|
||||
return Collections.singletonList(o.toString());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.strategy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 监听器表达式策略接口
|
||||
*
|
||||
* @author warm,battcn
|
||||
*/
|
||||
public interface ListenerStrategy extends ExpressionStrategy<Boolean> {
|
||||
|
||||
/**
|
||||
* 监听器表达式策略实现类集合
|
||||
*/
|
||||
List<ExpressionStrategy<Boolean>> EXPRESSION_STRATEGY_LIST = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
default void setExpression(ExpressionStrategy<Boolean> expressionStrategy) {
|
||||
EXPRESSION_STRATEGY_LIST.add(expressionStrategy);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.strategy;
|
||||
|
||||
import org.dromara.warm.flow.constant.FlowCons;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 票签表达式接口
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public interface VoteSignStrategy extends ExpressionStrategy<Boolean> {
|
||||
|
||||
/**
|
||||
* 票签表达式策略实现类集合
|
||||
*/
|
||||
List<ExpressionStrategy<Boolean>> EXPRESSION_STRATEGY_LIST = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
default void setExpression(ExpressionStrategy<Boolean> expressionStrategy) {
|
||||
EXPRESSION_STRATEGY_LIST.add(expressionStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
default String interceptStr() {
|
||||
return FlowCons.SPLIT_AT;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.config;
|
||||
|
||||
import org.dromara.warm.flow.ui.controller.WarmFlowController;
|
||||
import org.dromara.warm.flow.ui.controller.WarmFlowUiController;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* 工作流设计器配置类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "warm-flow.ui", havingValue = "true", matchIfMissing = true)
|
||||
@Import({WarmFlowUiController.class
|
||||
, WarmFlowController.class})
|
||||
public class WarmFlowUiConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
/** warm-flow配置 */
|
||||
registry.addResourceHandler("/warm-flow-ui/**")
|
||||
.addResourceLocations("classpath:/META-INF/resources/warm-flow-ui/", "classpath:/warm-flow-ui/");
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.controller;
|
||||
|
||||
import org.dromara.warm.flow.dto.ApiResult;
|
||||
import org.dromara.warm.flow.dto.DefJson;
|
||||
import org.dromara.warm.flow.dto.FlowDto;
|
||||
import org.dromara.warm.flow.entity.FlowInstance;
|
||||
import org.dromara.warm.flow.ui.dto.HandlerFeedBackDto;
|
||||
import org.dromara.warm.flow.ui.dto.HandlerQuery;
|
||||
import org.dromara.warm.flow.ui.service.WarmFlowService;
|
||||
import org.dromara.warm.flow.ui.vo.*;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 设计器Controller 可选择是否放行,放行可与业务系统共享权限,主要是用来访问业务系统数据
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/warm-flow")
|
||||
public class WarmFlowController {
|
||||
|
||||
/**
|
||||
* 保存流程json字符串
|
||||
*
|
||||
* @param defJson 流程数据集合
|
||||
* @return ApiResult<Void>
|
||||
* @throws Exception 异常
|
||||
* @author xiarg
|
||||
* @since 2024/10/29 16:31
|
||||
*/
|
||||
@PostMapping("/save-json")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ApiResult<Void> saveJson(@RequestBody DefJson defJson, @RequestHeader("onlyNodeSkip") boolean onlyNodeSkip) throws Exception {
|
||||
return WarmFlowService.saveJson(defJson, onlyNodeSkip);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程定义数据(包含节点和跳转)
|
||||
*
|
||||
* @param id 流程定义id
|
||||
* @return ApiResult<DefVo>
|
||||
* @author xiarg
|
||||
* @since 2024/10/29 16:31
|
||||
*/
|
||||
@GetMapping(value = {"/query-def", "/query-def/{id}"})
|
||||
public ApiResult<DefJson> queryDef(@PathVariable(value = "id", required = false) Long id) {
|
||||
return WarmFlowService.queryDef(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程图
|
||||
*
|
||||
* @param id 流程实例id
|
||||
* @return ApiResult<DefJson>
|
||||
*/
|
||||
@GetMapping("/query-flow-chart/{id}")
|
||||
public ApiResult<DefJson> queryFlowChart(@PathVariable("id") Long id) {
|
||||
return WarmFlowService.queryFlowChart(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 办理人权限设置列表tabs页签
|
||||
*
|
||||
* @return List<String>
|
||||
*/
|
||||
@GetMapping("/handler-type")
|
||||
public ApiResult<List<String>> handlerType() {
|
||||
return WarmFlowService.handlerType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 办理人权限设置列表结果
|
||||
*
|
||||
* @return HandlerSelectVo
|
||||
*/
|
||||
@GetMapping("/handler-result")
|
||||
public ApiResult<HandlerSelectVo> handlerResult(HandlerQuery query) {
|
||||
return WarmFlowService.handlerResult(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 办理人权限名称回显
|
||||
*
|
||||
* @return HandlerSelectVo
|
||||
*/
|
||||
@GetMapping("/handler-feedback")
|
||||
public ApiResult<List<HandlerFeedBackVo>> handlerFeedback(HandlerFeedBackDto handlerFeedBackDto) {
|
||||
return WarmFlowService.handlerFeedback(handlerFeedBackDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 办理人选择项
|
||||
*
|
||||
* @return List<Dict>
|
||||
*/
|
||||
@GetMapping("/handler-dict")
|
||||
public ApiResult<List<Dict>> handlerDict() {
|
||||
return WarmFlowService.handlerDict();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务id获取待办任务表单及数据
|
||||
*
|
||||
* @param taskId 当前任务id
|
||||
* @return {@link ApiResult< FlowDto >}
|
||||
* @author liangli
|
||||
* @date 2024/8/21 17:08
|
||||
**/
|
||||
@GetMapping(value = "/execute/load/{taskId}")
|
||||
public ApiResult<FlowDto> load(@PathVariable("taskId") Long taskId) {
|
||||
return WarmFlowService.load(taskId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务id获取已办任务表单及数据
|
||||
*
|
||||
* @param hisTaskId
|
||||
* @return
|
||||
*/
|
||||
@GetMapping(value = "/execute/hisLoad/{taskId}")
|
||||
public ApiResult<FlowDto> hisLoad(@PathVariable("taskId") Long hisTaskId) {
|
||||
return WarmFlowService.hisLoad(hisTaskId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用表单流程审批接口
|
||||
*
|
||||
* @param formData
|
||||
* @param taskId
|
||||
* @param skipType
|
||||
* @param message
|
||||
* @param nodeCode
|
||||
* @return
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@PostMapping(value = "/execute/handle")
|
||||
public ApiResult<FlowInstance> handle(@RequestBody Map<String, Object> formData, @RequestParam("taskId") Long taskId
|
||||
, @RequestParam("skipType") String skipType, @RequestParam("message") String message
|
||||
, @RequestParam(value = "nodeCode", required = false) String nodeCode) {
|
||||
return WarmFlowService.handle(formData, taskId, skipType, message, nodeCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点扩展属性
|
||||
*
|
||||
* @return List<NodeExt>
|
||||
*/
|
||||
@GetMapping("/node-ext")
|
||||
public ApiResult<List<NodeExt>> nodeExt() {
|
||||
return WarmFlowService.nodeExt();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点扩展属性
|
||||
*
|
||||
* @return List<NodeExt>
|
||||
*/
|
||||
@GetMapping("/listener-list")
|
||||
public ApiResult<List<ListenerVo>> listenerList() {
|
||||
return WarmFlowService.listenerList();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.controller;
|
||||
|
||||
import org.dromara.warm.flow.dto.ApiResult;
|
||||
import org.dromara.warm.flow.ui.service.WarmFlowService;
|
||||
import org.dromara.warm.flow.ui.vo.WarmFlowVo;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 设计器Controller 匿名访问
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/warm-flow-ui")
|
||||
public class WarmFlowUiController {
|
||||
|
||||
/**
|
||||
* 返回流程定义的配置
|
||||
*
|
||||
* @return ApiResult<WarmFlowVo>
|
||||
*/
|
||||
@GetMapping("/config")
|
||||
public ApiResult<WarmFlowVo> config() {
|
||||
return WarmFlowService.config();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程设计器-办理人选择
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
public class HandlerFeedBackDto {
|
||||
|
||||
/**
|
||||
* 入库主键集合,比如怕角色和用户id重复,可拼接为role:id
|
||||
*/
|
||||
private List<String> storageIds;
|
||||
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 办理人权限设置列表Function
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
public class HandlerFunDto<T> {
|
||||
/**
|
||||
* 权限列表
|
||||
*/
|
||||
private List<T> list;
|
||||
|
||||
/**
|
||||
* 权限列表list总数
|
||||
*/
|
||||
private long total;
|
||||
|
||||
/**
|
||||
* 获取入库主键集合Function
|
||||
*/
|
||||
private Function<T, String> storageId;
|
||||
|
||||
/**
|
||||
* 获取权限编码Function
|
||||
*/
|
||||
private Function<T, String> handlerCode;
|
||||
|
||||
/**
|
||||
* 获取权限名称Function
|
||||
*/
|
||||
private Function<T, String> handlerName;
|
||||
|
||||
/**
|
||||
* 获取权限分组名称Function
|
||||
*/
|
||||
private Function<T, String> groupName;
|
||||
|
||||
/**
|
||||
* 获取创建时间Function
|
||||
*/
|
||||
private Function<T, String> createTime;
|
||||
|
||||
public HandlerFunDto(List<T> list, long total) {
|
||||
this.list = list;
|
||||
this.total = total;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 流程设计器-办理人权限设置列表查询参数
|
||||
* 办理人权限列表选择框,可能存在多个,比如:部门、角色、用户的情况
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class HandlerQuery {
|
||||
|
||||
/**
|
||||
* 权限编码,如:zhang、roleAdmin、deptAdmin等编码
|
||||
*/
|
||||
private String handlerCode;
|
||||
|
||||
/**
|
||||
* 权限名称,如:管理员、角色管理员、部门管理员等名称
|
||||
*/
|
||||
private String handlerName;
|
||||
|
||||
/**
|
||||
* 办理权限类型,比如用户/角色/部门等
|
||||
*/
|
||||
private String handlerType;
|
||||
|
||||
/**
|
||||
* 页面左侧树权限分组主键,如:角色、部门等主键
|
||||
*/
|
||||
private String groupId;
|
||||
|
||||
/**
|
||||
* 当前页码
|
||||
*/
|
||||
private Integer pageNum;
|
||||
|
||||
/**
|
||||
* 每页显示条数
|
||||
*/
|
||||
private Integer pageSize;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private String beginTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private String endTime;
|
||||
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 页面左侧树列表Function
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
public class TreeFunDto<T> {
|
||||
|
||||
/**
|
||||
* 左侧树列表
|
||||
*/
|
||||
private List<T> list;
|
||||
|
||||
/**
|
||||
* 获取左侧树ID Function
|
||||
*/
|
||||
private Function<T, String> id;
|
||||
|
||||
/**
|
||||
* 获取左侧树名称 Function
|
||||
*/
|
||||
private Function<T, String> name;
|
||||
|
||||
/**
|
||||
* 获取左侧树父级ID Function
|
||||
*/
|
||||
private Function<T, String> parentId;
|
||||
|
||||
public TreeFunDto(List<T> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.service;
|
||||
|
||||
import org.dromara.warm.flow.dto.Tree;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分类接口
|
||||
*
|
||||
* @author warm
|
||||
* @since 2025/6/24
|
||||
*/
|
||||
public interface CategoryService {
|
||||
|
||||
/**
|
||||
* 查询分类
|
||||
*
|
||||
* @return 分类
|
||||
*/
|
||||
List<Tree> queryCategory();
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.service;
|
||||
|
||||
import org.dromara.warm.flow.dto.DefJson;
|
||||
import org.dromara.warm.flow.dto.PromptContent;
|
||||
import org.dromara.warm.flow.enums.NodeType;
|
||||
import org.dromara.warm.flow.utils.MapUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程图提示信息
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public interface ChartExtService {
|
||||
|
||||
|
||||
/**
|
||||
* 设置流程图提示信息
|
||||
*
|
||||
* @param defJson 流程定义json对象
|
||||
*/
|
||||
void execute(DefJson defJson);
|
||||
|
||||
/**
|
||||
* 初始化流程图提示信息
|
||||
*
|
||||
* @param defJson 流程定义json对象
|
||||
*/
|
||||
default void initPromptContent(DefJson defJson) {
|
||||
defJson.setTopText("流程名称: " + defJson.getFlowName());
|
||||
defJson.getNodeList().forEach(nodeJson -> {
|
||||
// 提示信息主对象
|
||||
PromptContent promptContent = new PromptContent();
|
||||
|
||||
if (NodeType.isGateWay(nodeJson.getNodeType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置 dialogStyle 样式
|
||||
promptContent.setDialogStyle(MapUtil.mergeAll(
|
||||
"position", "absolute",
|
||||
"backgroundColor", "#fff",
|
||||
"border", "1px solid #ccc",
|
||||
"borderRadius", "4px",
|
||||
"boxShadow", "0 2px 8px rgba(0, 0, 0, 0.15)",
|
||||
"padding", "8px 12px",
|
||||
"fontSize", "14px",
|
||||
"zIndex", 1000,
|
||||
"maxWidth", "500px",
|
||||
"color", "#333"
|
||||
));
|
||||
|
||||
// 创建 info 列表
|
||||
List<PromptContent.InfoItem> infoList = new ArrayList<>();
|
||||
|
||||
// 添加第一个条目: 任务名称
|
||||
PromptContent.InfoItem item = new PromptContent.InfoItem()
|
||||
.setPrefix("任务名称: ")
|
||||
.setContent(nodeJson.getNodeName())
|
||||
.setContentStyle(MapUtil.mergeAll("border", "1px solid #d1e9ff",
|
||||
"backgroundColor", "#e8f4ff",
|
||||
"padding", "4px 8px",
|
||||
"borderRadius", "4px"
|
||||
))
|
||||
.setRowStyle(MapUtil.mergeAll("fontWeight", "bold",
|
||||
"margin", "0 0 6px 0",
|
||||
"padding", "0 0 8px 0",
|
||||
"borderBottom", "1px solid #ccc"
|
||||
));
|
||||
infoList.add(item);
|
||||
promptContent.setInfo(infoList);
|
||||
|
||||
nodeJson.setPromptContent(promptContent);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.service;
|
||||
|
||||
import org.dromara.warm.flow.ui.vo.Dict;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程设计器-获取办理人选择项
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public interface HandlerDictService {
|
||||
|
||||
/**
|
||||
* 获取办理人选择项
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
List<Dict> getHandlerDict();
|
||||
}
|
||||
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.service;
|
||||
|
||||
import org.dromara.common.core.constant.HttpStatus;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import org.dromara.warm.flow.dto.FlowPage;
|
||||
import org.dromara.warm.flow.dto.Tree;
|
||||
import org.dromara.warm.flow.utils.*;
|
||||
import org.dromara.warm.flow.ui.dto.HandlerFunDto;
|
||||
import org.dromara.warm.flow.ui.dto.HandlerQuery;
|
||||
import org.dromara.warm.flow.ui.dto.TreeFunDto;
|
||||
import org.dromara.warm.flow.ui.utils.TreeUtil;
|
||||
import org.dromara.warm.flow.ui.vo.HandlerAuth;
|
||||
import org.dromara.warm.flow.ui.vo.HandlerFeedBackVo;
|
||||
import org.dromara.warm.flow.ui.vo.HandlerSelectVo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 流程设计器-获取办理人权限设置列表接口
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public interface HandlerSelectService {
|
||||
|
||||
/**
|
||||
* 获取办理人权限设置列表tabs页签,如:用户、角色和部门等,可以返回其中一种或者多种,按业务需求决定
|
||||
*
|
||||
* @return tabs页签
|
||||
*/
|
||||
List<String> getHandlerType();
|
||||
|
||||
/**
|
||||
* 获取用户列表、角色列表、部门列表等,可以返回其中一种或者多种,按业务需求决定
|
||||
*
|
||||
* @param query 查询参数
|
||||
* @return 结果
|
||||
*/
|
||||
HandlerSelectVo getHandlerSelect(HandlerQuery query);
|
||||
|
||||
/**
|
||||
* 办理人权限名称回显,兼容老项目,新项目重写提高性能
|
||||
*
|
||||
* @param storageIds 入库主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
default List<HandlerFeedBackVo> handlerFeedback(List<String> storageIds) {
|
||||
List<HandlerFeedBackVo> handlerFeedBackVos = new ArrayList<>();
|
||||
if (CollUtil.isEmpty(storageIds)) {
|
||||
return handlerFeedBackVos;
|
||||
}
|
||||
Map<String, String> authMap = new HashMap<>();
|
||||
List<String> handlerTypes = getHandlerType();
|
||||
if (CollUtil.isEmpty(handlerTypes)) {
|
||||
return handlerFeedBackVos;
|
||||
}
|
||||
for (String handlerType : handlerTypes) {
|
||||
HandlerQuery handlerQuery = new HandlerQuery();
|
||||
handlerQuery.setHandlerType(handlerType);
|
||||
HandlerSelectVo handlerSelectVo = getHandlerSelect(handlerQuery);
|
||||
if (ObjectUtil.isNotNull(handlerSelectVo)) {
|
||||
FlowPage<HandlerAuth> handlerAuths = handlerSelectVo.getHandlerAuths();
|
||||
List<HandlerAuth> rows = handlerAuths.getRows();
|
||||
if (CollUtil.isNotEmpty(rows)) {
|
||||
authMap.putAll(StreamUtils.toMap(rows, HandlerAuth::getStorageId, HandlerAuth::getHandlerName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历storageIds,按照原本的顺序回显名称
|
||||
for (String storageId : storageIds) {
|
||||
handlerFeedBackVos.add(new HandlerFeedBackVo(storageId
|
||||
, MapUtil.isEmpty(authMap) ? "" : authMap.get(storageId)));
|
||||
}
|
||||
return handlerFeedBackVos;
|
||||
}
|
||||
|
||||
default <T> HandlerSelectVo getHandlerSelectVo(HandlerFunDto<T> handlerFunDto) {
|
||||
List<HandlerAuth> handlerAuths = new ArrayList<>();
|
||||
// 遍历角色数据,封装为组件可识别的数据
|
||||
for (T obj : handlerFunDto.getList()) {
|
||||
handlerAuths.add(new HandlerAuth()
|
||||
.setStorageId(handlerFunDto.getStorageId() == null ? null : handlerFunDto.getStorageId().apply(obj))
|
||||
.setHandlerCode(handlerFunDto.getHandlerCode() == null ? null : handlerFunDto.getHandlerCode().apply(obj))
|
||||
.setHandlerName(handlerFunDto.getHandlerName() == null ? null : handlerFunDto.getHandlerName().apply(obj))
|
||||
.setCreateTime(handlerFunDto.getCreateTime() == null ? null : handlerFunDto.getCreateTime().apply(obj))
|
||||
.setGroupName(handlerFunDto.getGroupName() == null ? null : handlerFunDto.getGroupName().apply(obj)));
|
||||
}
|
||||
return getResult(handlerAuths, handlerFunDto.getTotal());
|
||||
}
|
||||
|
||||
default <T, R> HandlerSelectVo getHandlerSelectVo(HandlerFunDto<T> handlerFunDto, TreeFunDto<R> treeFunDto) {
|
||||
HandlerSelectVo handlerSelectVo = getHandlerSelectVo(handlerFunDto);
|
||||
|
||||
List<Tree> treeList = StreamUtils.toList(treeFunDto.getList(), org ->
|
||||
new Tree().setId(treeFunDto.getId() == null ? null : treeFunDto.getId().apply(org))
|
||||
.setName(treeFunDto.getName() == null ? null : treeFunDto.getName().apply(org))
|
||||
.setParentId(treeFunDto.getParentId() == null ? null : treeFunDto.getParentId().apply(org)));
|
||||
|
||||
// 通过递归,构建树状结构
|
||||
return handlerSelectVo.setTreeSelections(TreeUtil.buildTree(treeList));
|
||||
}
|
||||
|
||||
|
||||
default HandlerSelectVo getResult(List<HandlerAuth> handlerAuths, long total) {
|
||||
return new HandlerSelectVo().setHandlerAuths(new FlowPage<HandlerAuth>()
|
||||
.setCode(HttpStatus.SUCCESS)
|
||||
.setMsg("查询成功")
|
||||
.setRows(handlerAuths)
|
||||
.setTotal(total));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.service;
|
||||
|
||||
import org.dromara.warm.flow.ui.vo.ListenerVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程设计器-获取监听器列表
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public interface ListenerListService {
|
||||
|
||||
/**
|
||||
* 获取监听器列表
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
List<ListenerVo> listenerList();
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.service;
|
||||
|
||||
import org.dromara.warm.flow.ui.vo.NodeExt;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程设计器-节点扩展属性
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
public interface NodeExtService {
|
||||
|
||||
/**
|
||||
* 获取节点扩展属性
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
List<NodeExt> getNodeExt();
|
||||
}
|
||||
@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.service;
|
||||
|
||||
import org.dromara.warm.flow.json.JsonUtil;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.warm.flow.FlowEngine;
|
||||
import org.dromara.warm.flow.config.WarmFlowProperties;
|
||||
import org.dromara.warm.flow.dto.*;
|
||||
import org.dromara.warm.flow.entity.FlowInstance;
|
||||
import org.dromara.warm.flow.enums.FormCustomEnum;
|
||||
import org.dromara.warm.flow.enums.ModelEnum;
|
||||
import org.dromara.warm.flow.exception.FlowException;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
|
||||
import org.dromara.warm.flow.ui.dto.HandlerFeedBackDto;
|
||||
import org.dromara.warm.flow.ui.dto.HandlerQuery;
|
||||
import org.dromara.warm.flow.ui.utils.TreeUtil;
|
||||
import org.dromara.warm.flow.ui.vo.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 设计器Controller 可选择是否放行,放行可与业务系统共享权限,主要是用来访问业务系统数据
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@Slf4j
|
||||
public class WarmFlowService {
|
||||
|
||||
/**
|
||||
* 返回流程定义的配置
|
||||
*
|
||||
* @return ApiResult<WarmFlowVo>
|
||||
*/
|
||||
public static ApiResult<WarmFlowVo> config() {
|
||||
WarmFlowVo warmFlowVo = new WarmFlowVo();
|
||||
WarmFlowProperties warmFlow = FlowEngine.getFlowConfig();
|
||||
warmFlowVo.setFramework(warmFlow.getFramework().name());
|
||||
// 获取tokenName
|
||||
String tokenName = warmFlow.getTokenName();
|
||||
if (StrUtil.isEmpty(tokenName)) {
|
||||
return ApiResult.fail("未配置tokenName");
|
||||
}
|
||||
String[] tokenNames = tokenName.split(",");
|
||||
List<String> tokenNameList = Arrays.stream(tokenNames).filter(StrUtil::isNotEmpty)
|
||||
.map(String::trim).collect(Collectors.toList());
|
||||
warmFlowVo.setTokenNameList(tokenNameList);
|
||||
|
||||
return ApiResult.ok(warmFlowVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存流程json字符串
|
||||
*
|
||||
* @param defJson 流程数据集合
|
||||
* @param onlyNodeSkip 是否只保存节点和跳转
|
||||
* @return ApiResult<Void>
|
||||
* @throws Exception 异常
|
||||
* @author xiarg
|
||||
* @since 2024/10/29 16:31
|
||||
*/
|
||||
public static ApiResult<Void> saveJson(DefJson defJson, boolean onlyNodeSkip) throws Exception {
|
||||
FlowEngine.defService().saveDef(defJson, onlyNodeSkip);
|
||||
return ApiResult.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程定义数据(包含节点和跳转)
|
||||
*
|
||||
* @param id 流程定义id
|
||||
* @return ApiResult<DefVo>
|
||||
* @author xiarg
|
||||
* @since 2024/10/29 16:31
|
||||
*/
|
||||
public static ApiResult<DefJson> queryDef(Long id) {
|
||||
try {
|
||||
DefJson defJson;
|
||||
if (id == null) {
|
||||
defJson = new DefJson()
|
||||
.setModelValue(ModelEnum.CLASSICS.name())
|
||||
.setFormCustom(FormCustomEnum.N.name());
|
||||
} else {
|
||||
defJson = FlowEngine.defService().queryDesign(id);
|
||||
}
|
||||
CategoryService categoryService = SpringUtils.getBeanOrNull(CategoryService.class);
|
||||
if (categoryService != null) {
|
||||
List<Tree> treeList = categoryService.queryCategory();
|
||||
defJson.setCategoryList(TreeUtil.buildTree(treeList));
|
||||
}
|
||||
return ApiResult.ok(defJson);
|
||||
} catch (Exception e) {
|
||||
log.error("获取流程json字符串", e);
|
||||
throw new FlowException("获取流程json字符串失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程图
|
||||
*
|
||||
* @param id 流程实例id
|
||||
* @return ApiResult<DefJson>
|
||||
*/
|
||||
public static ApiResult<DefJson> queryFlowChart(Long id) {
|
||||
try {
|
||||
FlowInstance instance = FlowEngine.insService().getById(id);
|
||||
String defJsonStr = instance.getDefJson();
|
||||
DefJson defJson = JsonUtil.strToBean(defJsonStr, DefJson.class);
|
||||
defJson.setInstance(instance);
|
||||
|
||||
// 获取流程图三原色
|
||||
defJson.setChartStatusColor(FlowEngine.chartService().getChartRgb(defJson.getModelValue()));
|
||||
// 是否显示流程图顶部文字
|
||||
defJson.setTopTextShow(FlowEngine.getFlowConfig().isTopTextShow());
|
||||
// 需要业务系统实现该接口
|
||||
ChartExtService chartExtService = SpringUtils.getBeanOrNull(ChartExtService.class);
|
||||
if (chartExtService != null) {
|
||||
chartExtService.initPromptContent(defJson);
|
||||
chartExtService.execute(defJson);
|
||||
}
|
||||
|
||||
return ApiResult.ok(defJson);
|
||||
} catch (Exception e) {
|
||||
log.error("获取流程图", e);
|
||||
throw new FlowException("获取流程图失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 办理人权限设置列表tabs页签
|
||||
*
|
||||
* @return List<String>
|
||||
*/
|
||||
public static ApiResult<List<String>> handlerType() {
|
||||
try {
|
||||
// 需要业务系统实现该接口
|
||||
HandlerSelectService handlerSelectService = SpringUtils.getBeanOrNull(HandlerSelectService.class);
|
||||
if (handlerSelectService == null) {
|
||||
return ApiResult.ok(Collections.emptyList());
|
||||
}
|
||||
List<String> handlerType = handlerSelectService.getHandlerType();
|
||||
return ApiResult.ok(handlerType);
|
||||
} catch (Exception e) {
|
||||
log.error("办理人权限设置列表tabs页签异常", e);
|
||||
throw new FlowException("办理人权限设置列表tabs页签失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 办理人权限设置列表结果
|
||||
*
|
||||
* @return HandlerSelectVo
|
||||
*/
|
||||
public static ApiResult<HandlerSelectVo> handlerResult(HandlerQuery query) {
|
||||
try {
|
||||
// 需要业务系统实现该接口
|
||||
HandlerSelectService handlerSelectService = SpringUtils.getBeanOrNull(HandlerSelectService.class);
|
||||
if (handlerSelectService == null) {
|
||||
return ApiResult.ok(new HandlerSelectVo());
|
||||
}
|
||||
HandlerSelectVo handlerSelectVo = handlerSelectService.getHandlerSelect(query);
|
||||
return ApiResult.ok(handlerSelectVo);
|
||||
} catch (Exception e) {
|
||||
log.error("办理人权限设置列表结果异常", e);
|
||||
throw new FlowException("办理人权限设置列表结果失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 办理人权限名称回显
|
||||
*
|
||||
* @return HandlerSelectVo
|
||||
*/
|
||||
public static ApiResult<List<HandlerFeedBackVo>> handlerFeedback(HandlerFeedBackDto handlerFeedBackDto) {
|
||||
try {
|
||||
// 需要业务系统实现该接口
|
||||
HandlerSelectService handlerSelectService = SpringUtils.getBeanOrNull(HandlerSelectService.class);
|
||||
if (handlerSelectService == null) {
|
||||
List<HandlerFeedBackVo> handlerFeedBackVos = StreamUtils.toList(handlerFeedBackDto.getStorageIds(),
|
||||
storageId -> new HandlerFeedBackVo(storageId, null));
|
||||
return ApiResult.ok(handlerFeedBackVos);
|
||||
}
|
||||
List<HandlerFeedBackVo> handlerFeedBackVos = handlerSelectService.handlerFeedback(handlerFeedBackDto.getStorageIds());
|
||||
return ApiResult.ok(handlerFeedBackVos);
|
||||
} catch (Exception e) {
|
||||
log.error("办理人权限名称回显", e);
|
||||
throw new FlowException("办理人权限名称回显", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 办理人选择项
|
||||
*
|
||||
* @return List<Dict>
|
||||
*/
|
||||
public static ApiResult<List<Dict>> handlerDict() {
|
||||
try {
|
||||
// 需要业务系统实现该接口
|
||||
HandlerDictService handlerDictService = SpringUtils.getBeanOrNull(HandlerDictService.class);
|
||||
if (handlerDictService == null) {
|
||||
List<Dict> dictList = new ArrayList<>();
|
||||
Dict dict = new Dict();
|
||||
dict.setLabel("默认表达式");
|
||||
dict.setValue("${handler}");
|
||||
Dict dict1 = new Dict();
|
||||
dict1.setLabel("spel表达式");
|
||||
dict1.setValue("#{@user.evalVar(#handler)}");
|
||||
Dict dict2 = new Dict();
|
||||
dict2.setLabel("其他");
|
||||
dict2.setValue("");
|
||||
dictList.add(dict);
|
||||
dictList.add(dict1);
|
||||
dictList.add(dict2);
|
||||
|
||||
return ApiResult.ok(dictList);
|
||||
}
|
||||
return ApiResult.ok(handlerDictService.getHandlerDict());
|
||||
} catch (Exception e) {
|
||||
log.error("办理人权限设置列表结果异常", e);
|
||||
throw new FlowException("办理人权限设置列表结果失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务id获取待办任务表单及数据
|
||||
*
|
||||
* @param taskId 当前任务id
|
||||
* @return {@link ApiResult<FlowDto>}
|
||||
* @author liangli
|
||||
* @date 2024/8/21 17:08
|
||||
**/
|
||||
public static ApiResult<FlowDto> load(Long taskId) {
|
||||
FlowParams flowParams = FlowParams.build();
|
||||
|
||||
return ApiResult.ok(FlowEngine.taskService().load(taskId, flowParams));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务id获取已办任务表单及数据
|
||||
*
|
||||
* @param hisTaskId
|
||||
* @return
|
||||
*/
|
||||
public static ApiResult<FlowDto> hisLoad(Long hisTaskId) {
|
||||
FlowParams flowParams = FlowParams.build();
|
||||
|
||||
return ApiResult.ok(FlowEngine.taskService().hisLoad(hisTaskId, flowParams));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用表单流程审批接口
|
||||
*
|
||||
* @param formData
|
||||
* @param taskId
|
||||
* @param skipType
|
||||
* @param message
|
||||
* @param nodeCode
|
||||
* @return
|
||||
*/
|
||||
public static ApiResult<FlowInstance> handle(Map<String, Object> formData, Long taskId, String skipType
|
||||
, String message, String nodeCode) {
|
||||
FlowParams flowParams = FlowParams.build()
|
||||
.skipType(skipType)
|
||||
.nodeCode(nodeCode)
|
||||
.message(message);
|
||||
|
||||
flowParams.formData(formData);
|
||||
|
||||
return ApiResult.ok(FlowEngine.taskService().skip(taskId, flowParams));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点扩展属性
|
||||
*
|
||||
* @return List<NodeExt>
|
||||
*/
|
||||
public static ApiResult<List<NodeExt>> nodeExt() {
|
||||
try {
|
||||
// 需要业务系统实现该接口
|
||||
NodeExtService nodeExtService = SpringUtils.getBeanOrNull(NodeExtService.class);
|
||||
if (nodeExtService == null) {
|
||||
return ApiResult.ok(Collections.emptyList());
|
||||
}
|
||||
List<NodeExt> nodeExts = nodeExtService.getNodeExt();
|
||||
return ApiResult.ok(nodeExts);
|
||||
} catch (Exception e) {
|
||||
log.error("获取节点扩展属性", e);
|
||||
throw new FlowException("获取节点扩展属性失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取监听器列表
|
||||
*
|
||||
* @return List<NodeExt>
|
||||
*/
|
||||
public static ApiResult<List<ListenerVo>> listenerList() {
|
||||
try {
|
||||
// 需要业务系统实现该接口
|
||||
ListenerListService listenerListService = SpringUtils.getBeanOrNull(ListenerListService.class);
|
||||
if (listenerListService == null) {
|
||||
return ApiResult.ok(Collections.emptyList());
|
||||
}
|
||||
List<ListenerVo> listenerList = listenerListService.listenerList();
|
||||
return ApiResult.ok(listenerList);
|
||||
} catch (Exception e) {
|
||||
log.error("获取监听器列表", e);
|
||||
throw new FlowException("获取监听器列表失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.utils;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import org.dromara.warm.flow.dto.Tree;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TreeUtil {
|
||||
private TreeUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建所需要树结构
|
||||
*
|
||||
* @param trees 部门列表
|
||||
* @return 树结构列表
|
||||
*/
|
||||
public static List<Tree> buildTree(List<Tree> trees) {
|
||||
List<Tree> returnList = new ArrayList<>();
|
||||
List<String> tempList = trees.stream().map(Tree::getId).collect(Collectors.toList());
|
||||
for (Tree dept : trees) {
|
||||
// 如果是顶级节点, 遍历该父节点的所有子节点
|
||||
if (!tempList.contains(dept.getParentId())) {
|
||||
recursionFn(trees, dept);
|
||||
returnList.add(dept);
|
||||
}
|
||||
}
|
||||
if (returnList.isEmpty()) {
|
||||
returnList = trees;
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归列表
|
||||
*/
|
||||
private static void recursionFn(List<Tree> list, Tree t) {
|
||||
// 得到子节点列表
|
||||
List<Tree> childList = getChildList(list, t);
|
||||
t.setChildren(childList);
|
||||
for (Tree tChild : childList) {
|
||||
if (hasChild(list, tChild)) {
|
||||
recursionFn(list, tChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否有子节点
|
||||
*/
|
||||
private static boolean hasChild(List<Tree> list, Tree t) {
|
||||
return !getChildList(list, t).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到子节点列表
|
||||
*/
|
||||
private static List<Tree> getChildList(List<Tree> list, Tree t) {
|
||||
List<Tree> tlist = new ArrayList<>();
|
||||
for (Tree n : list) {
|
||||
if (StrUtil.isNotEmpty(n.getParentId()) && n.getParentId().equals(t.getId())) {
|
||||
tlist.add(n);
|
||||
}
|
||||
}
|
||||
return tlist;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.vo;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字典
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class Dict implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 字典标签
|
||||
*/
|
||||
private String label;
|
||||
|
||||
/**
|
||||
* 字典值
|
||||
*/
|
||||
private String value;
|
||||
|
||||
private List<Dict> childList;
|
||||
|
||||
public Dict(String label, String value) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.vo;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 流程设计器-办理人权限设置列表
|
||||
* 办理人权限列表选择框,可能存在多个,比如:部门、角色、用户的情况
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
public class HandlerAuth {
|
||||
|
||||
/**
|
||||
* 入库主键,比如怕角色和用户id重复,可拼接为role:id
|
||||
*/
|
||||
private String storageId;
|
||||
|
||||
/**
|
||||
* 权限编码,如:zhang、roleAdmin、deptAdmin等编码
|
||||
*/
|
||||
private String handlerCode;
|
||||
|
||||
/**
|
||||
* 权限名称,如:管理员、角色管理员、部门管理员等名称
|
||||
*/
|
||||
private String handlerName;
|
||||
|
||||
/**
|
||||
* 权限分组名称,如:角色、部门等名称
|
||||
*/
|
||||
private String groupName;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private String createTime;
|
||||
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 流程设计器-办理人选择
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public class HandlerFeedBackVo {
|
||||
|
||||
/**
|
||||
* 入库主键,比如怕角色和用户id重复,可拼接为role:id
|
||||
*/
|
||||
private String storageId;
|
||||
|
||||
/**
|
||||
* 权限名称,如:管理员、角色管理员、部门管理员等名称
|
||||
*/
|
||||
private String handlerName;
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.vo;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.warm.flow.dto.FlowPage;
|
||||
import org.dromara.warm.flow.dto.Tree;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程设计器-办理人选择
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
public class HandlerSelectVo {
|
||||
|
||||
/**
|
||||
* 办理人选择,分页列表,有具体办理对象 比如:部门、角色和用户等情况
|
||||
*/
|
||||
private FlowPage<HandlerAuth> handlerAuths;
|
||||
|
||||
/**
|
||||
* 左侧树状选择,配合{@link #handlerAuths}使用,比如用户先选择部门,然后选择用户
|
||||
*/
|
||||
private List<Tree> treeSelections;
|
||||
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 监听器列表-设计器页面,监听器列表下拉选使用
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ListenerVo implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 监听器类型,如:start、assignment、finish、create,如果未设置,选择监听器后,监听器类型不联动
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 监听器全限定类路径
|
||||
*/
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 监听器描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.vo;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 节点扩展属性
|
||||
*
|
||||
* @author warm
|
||||
* @since 2025/2/18
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class NodeExt implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String code;
|
||||
private String name;
|
||||
private String desc;
|
||||
private int type;
|
||||
private List<ChildNode> childs;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class ChildNode {
|
||||
private String code;
|
||||
private String desc;
|
||||
private String label;
|
||||
private int type;
|
||||
private boolean must;
|
||||
private boolean multiple;
|
||||
private int precision;
|
||||
private String step;
|
||||
private String min;
|
||||
private String dateType;
|
||||
private String dateFormat;
|
||||
private List<DictItem> dict;
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class DictItem {
|
||||
private String label;
|
||||
private String value;
|
||||
private boolean selected;
|
||||
|
||||
public DictItem() {
|
||||
}
|
||||
|
||||
public DictItem(String label, String value) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public DictItem(String label, String value, boolean selected) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
this.selected = selected;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.ui.vo;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程配置vo
|
||||
*
|
||||
* @author warm
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
public class WarmFlowVo {
|
||||
|
||||
/**
|
||||
* 如果需要工作流共享业务系统权限,默认Authorization
|
||||
*/
|
||||
private List<String> tokenNameList;
|
||||
|
||||
/**
|
||||
* 框架类型: springboot、solon
|
||||
*/
|
||||
private String framework;
|
||||
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2024-2025, Warm-Flow (290631660@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.warm.flow.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import org.dromara.warm.flow.exception.FlowException;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Assert类
|
||||
*
|
||||
* @author warm
|
||||
* @since 2023/3/30 14:05
|
||||
*/
|
||||
public class AssertUtil {
|
||||
private AssertUtil() {
|
||||
|
||||
}
|
||||
|
||||
public static void isNull(Object obj, String errorMsg) {
|
||||
if (obj == null) {
|
||||
throw new FlowException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void isNotNull(Object obj, String errorMsg) {
|
||||
if (obj != null) {
|
||||
throw new FlowException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为true不抛异常
|
||||
*
|
||||
* @param obj
|
||||
* @param errorMsg
|
||||
*/
|
||||
public static void isFalse(boolean obj, String errorMsg) {
|
||||
if (!obj) {
|
||||
throw new FlowException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为false不抛异常
|
||||
*
|
||||
* @param obj
|
||||
* @param errorMsg
|
||||
*/
|
||||
public static void isTrue(boolean obj, String errorMsg) {
|
||||
if (obj) {
|
||||
throw new FlowException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void isNotEmpty(Object obj, String errorMsg) {
|
||||
if (obj != null) {
|
||||
if (obj instanceof String) {
|
||||
AssertUtil.isTrue(StrUtil.isNotEmpty((String) obj), errorMsg);
|
||||
} else if (obj instanceof Collection) {
|
||||
AssertUtil.isTrue(CollUtil.isNotEmpty((Collection<?>) obj), errorMsg);
|
||||
} else if (obj instanceof Map) {
|
||||
AssertUtil.isTrue(MapUtil.isNotEmpty((Map<?, ?>) obj), errorMsg);
|
||||
} else {
|
||||
throw new FlowException("Unsupported type: " + obj.getClass().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void isEmpty(Object obj, String errorMsg) {
|
||||
if (obj == null) {
|
||||
throw new FlowException(errorMsg);
|
||||
} else if (obj instanceof String) {
|
||||
AssertUtil.isTrue(StrUtil.isEmpty((String) obj), errorMsg);
|
||||
} else if (obj instanceof Collection) {
|
||||
AssertUtil.isTrue(CollUtil.isEmpty((Collection<?>) obj), errorMsg);
|
||||
} else if (obj instanceof Map) {
|
||||
AssertUtil.isTrue(MapUtil.isEmpty((Map<?, ?>) obj), errorMsg);
|
||||
} else {
|
||||
throw new FlowException("Unsupported type: " + obj.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> void contains(Collection<T> a, T b, String errorMsg) {
|
||||
if (CollUtil.isNotEmpty(a) && a.contains(b)) {
|
||||
throw new FlowException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> void notContains(Collection<T> a, T b, String errorMsg) {
|
||||
if (CollUtil.isEmpty(a) || !a.contains(b)) {
|
||||
throw new FlowException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user