init: 导入RuoYi‑Vue‑Plus 6.X完整代码
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-modules</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-workflow</artifactId>
|
||||
|
||||
<description>
|
||||
工作流模块
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- 消息推送模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-push</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 接口模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-doc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 邮件服务 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 短信模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-sms</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据库服务 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- web服务 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 日志记录 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-log</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- excel -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 翻译模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-translation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- api模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Warm-Flow 工作流引擎 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara.warm</groupId>
|
||||
<artifactId>warm-flow-mybatis-plus-sb3-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>
|
||||
<artifactId>ruoyi-common-liteflow</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package org.dromara.workflow.common;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 自定义条件注解,用于基于配置启用或禁用特定功能
|
||||
* <p>
|
||||
* 该注解只会在配置文件中 `warm-flow.enabled=true` 时,标注了此注解的类或方法才会被 Spring 容器加载
|
||||
* <p>
|
||||
* 示例配置:
|
||||
* <pre>
|
||||
* warm-flow:
|
||||
* enabled: true # 设置为 true 时,启用工作流功能
|
||||
* </pre>
|
||||
* <p>
|
||||
* 使用此注解时,可以动态控制工作流功能是否启用,而不需要修改代码逻辑
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@ConditionalOnProperty(value = "warm-flow.enabled", havingValue = "true")
|
||||
public @interface ConditionalOnEnable {
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package org.dromara.workflow.common.constant;
|
||||
|
||||
|
||||
/**
|
||||
* 工作流常量
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
public interface FlowConstant {
|
||||
|
||||
/**
|
||||
* 流程发起人
|
||||
*/
|
||||
String INITIATOR = "initiator";
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
String BUSINESS_ID = "businessId";
|
||||
|
||||
/**
|
||||
* 部门id
|
||||
*/
|
||||
String INITIATOR_DEPT_ID = "initiatorDeptId";
|
||||
|
||||
/**
|
||||
* 流程分类 ID 转名称的翻译标识。
|
||||
*/
|
||||
String CATEGORY_ID_TO_NAME = "category_id_to_name";
|
||||
|
||||
/**
|
||||
* 流程分类名称缓存键。
|
||||
*/
|
||||
String FLOW_CATEGORY_NAME = "flow_category_name#30d";
|
||||
|
||||
/**
|
||||
* 默认租户 OA 申请分类 ID。
|
||||
*/
|
||||
Long FLOW_CATEGORY_ID = 100L;
|
||||
|
||||
/**
|
||||
* 申请人提交动作标识。
|
||||
*/
|
||||
String SUBMIT = "submit";
|
||||
|
||||
/**
|
||||
* 抄送列表流程变量名。
|
||||
*/
|
||||
String FLOW_COPY_LIST = "flowCopyList";
|
||||
|
||||
/**
|
||||
* 消息类型流程变量名。
|
||||
*/
|
||||
String MESSAGE_TYPE = "messageType";
|
||||
|
||||
/**
|
||||
* 消息通知内容流程变量名。
|
||||
*/
|
||||
String MESSAGE_NOTICE = "messageNotice";
|
||||
|
||||
/**
|
||||
* 我的发起页面路径。
|
||||
*/
|
||||
String PATH_MY_DOCUMENT = "/task/myDocument";
|
||||
|
||||
/**
|
||||
* 我的待办页面路径。
|
||||
*/
|
||||
String PATH_TASK_WAITING = "/task/taskWaiting";
|
||||
|
||||
/**
|
||||
* 我的已办页面路径。
|
||||
*/
|
||||
String PATH_TASK_FINISH = "/task/taskFinish";
|
||||
|
||||
/**
|
||||
* 我的抄送页面路径。
|
||||
*/
|
||||
String PATH_TASK_COPY = "/task/taskCopyList";
|
||||
|
||||
/**
|
||||
* 任务状态字典类型编码。
|
||||
*/
|
||||
String WF_TASK_STATUS = "wf_task_status";
|
||||
|
||||
/**
|
||||
* 自动通过变量标识。
|
||||
*/
|
||||
String AUTO_PASS = "autoPass";
|
||||
|
||||
/**
|
||||
* 业务编码流程变量名。
|
||||
*/
|
||||
String BUSINESS_CODE = "businessCode";
|
||||
|
||||
/**
|
||||
* 忽略-办理权限校验(true:忽略,false:不忽略)
|
||||
*/
|
||||
String VAR_IGNORE = "ignore";
|
||||
|
||||
/**
|
||||
* 忽略-委派处理(true:忽略,false:不忽略)
|
||||
*/
|
||||
String VAR_IGNORE_DEPUTE = "ignoreDepute";
|
||||
|
||||
/**
|
||||
* 忽略-会签票签处理(true:忽略,false:不忽略)
|
||||
*/
|
||||
String VAR_IGNORE_COOPERATE = "ignoreCooperate";
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package org.dromara.workflow.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 按钮权限枚举
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ButtonPermissionEnum implements NodeExtEnum {
|
||||
|
||||
/**
|
||||
* 是否弹窗选人
|
||||
*/
|
||||
POP("是否弹窗选人", "pop", false),
|
||||
|
||||
/**
|
||||
* 是否能委托
|
||||
*/
|
||||
TRUST("是否能委托", "trust", false),
|
||||
|
||||
/**
|
||||
* 是否能转办
|
||||
*/
|
||||
TRANSFER("是否能转办", "transfer", false),
|
||||
|
||||
/**
|
||||
* 是否能抄送
|
||||
*/
|
||||
COPY("是否能抄送", "copy", true),
|
||||
|
||||
/**
|
||||
* 是否显示退回
|
||||
*/
|
||||
BACK("是否显示退回", "back", true),
|
||||
|
||||
/**
|
||||
* 是否能加签
|
||||
*/
|
||||
ADD_SIGN("是否能加签", "addSign", false),
|
||||
|
||||
/**
|
||||
* 是否能减签
|
||||
*/
|
||||
SUB_SIGN("是否能减签", "subSign", false),
|
||||
|
||||
/**
|
||||
* 是否能终止
|
||||
*/
|
||||
TERMINATION("是否能终止", "termination", true),
|
||||
|
||||
/**
|
||||
* 是否能上传附件
|
||||
*/
|
||||
FILE("是否能上传附件", "file", true);
|
||||
|
||||
/**
|
||||
* 按钮展示名称。
|
||||
*/
|
||||
private final String label;
|
||||
|
||||
/**
|
||||
* 按钮权限值。
|
||||
*/
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* 是否默认开启。
|
||||
*/
|
||||
private final boolean selected;
|
||||
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package org.dromara.workflow.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 抄送设置枚举
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum CopySettingEnum implements NodeExtEnum {
|
||||
;
|
||||
|
||||
/**
|
||||
* 展示名称。
|
||||
*/
|
||||
private final String label;
|
||||
|
||||
/**
|
||||
* 配置值。
|
||||
*/
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* 是否默认选中。
|
||||
*/
|
||||
private final boolean selected;
|
||||
|
||||
}
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package org.dromara.workflow.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 消息类型枚举,定义流程通知支持的消息通道。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum MessageTypeEnum {
|
||||
|
||||
/**
|
||||
* 站内信
|
||||
*/
|
||||
SYSTEM_MESSAGE("1", "站内信"),
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
EMAIL_MESSAGE("2", "邮箱"),
|
||||
|
||||
/**
|
||||
* 短信
|
||||
*/
|
||||
SMS_MESSAGE("3", "短信");
|
||||
|
||||
/**
|
||||
* 消息类型编码。
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* 消息类型描述。
|
||||
*/
|
||||
private final String desc;
|
||||
|
||||
/**
|
||||
* 消息类型编码缓存。
|
||||
*/
|
||||
private static final Map<String, MessageTypeEnum> MESSAGE_TYPE_ENUM_MAP = Arrays.stream(values())
|
||||
.collect(Collectors.toConcurrentMap(MessageTypeEnum::getCode, Function.identity()));
|
||||
|
||||
/**
|
||||
* 根据消息类型编码获取枚举实例。
|
||||
*
|
||||
* @param code 消息类型code
|
||||
* @return MessageTypeEnum
|
||||
*/
|
||||
public static MessageTypeEnum getByCode(String code) {
|
||||
return MESSAGE_TYPE_ENUM_MAP.getOrDefault(code, null);
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package org.dromara.workflow.common.enums;
|
||||
|
||||
/**
|
||||
* 节点扩展属性枚举通用接口,约束扩展选项的展示名、值和默认勾选状态。
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
public interface NodeExtEnum {
|
||||
|
||||
/**
|
||||
* 获取选项展示名称。
|
||||
*
|
||||
* @return 选项label
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* 获取选项值。
|
||||
*
|
||||
* @return 选项值
|
||||
*/
|
||||
String getValue();
|
||||
|
||||
/**
|
||||
* 是否默认选中。
|
||||
*
|
||||
* @return 是否默认选中
|
||||
*/
|
||||
boolean isSelected();
|
||||
|
||||
}
|
||||
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package org.dromara.workflow.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 任务分配人枚举
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum TaskAssigneeEnum {
|
||||
|
||||
/**
|
||||
* 用户
|
||||
*/
|
||||
USER("用户", ""),
|
||||
|
||||
/**
|
||||
* 角色
|
||||
*/
|
||||
ROLE("角色", "role:"),
|
||||
|
||||
/**
|
||||
* 部门
|
||||
*/
|
||||
DEPT("部门", "dept:"),
|
||||
|
||||
/**
|
||||
* 岗位
|
||||
*/
|
||||
POST("岗位", "post:"),
|
||||
|
||||
/**
|
||||
* SPEL表达式
|
||||
*/
|
||||
SPEL("SpEL表达式", "");
|
||||
|
||||
/**
|
||||
* 类型描述。
|
||||
*/
|
||||
private final String desc;
|
||||
|
||||
/**
|
||||
* 类型编码前缀。
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* 根据描述获取对应的枚举类型
|
||||
* <p>
|
||||
* 通过传入描述,查找并返回匹配的枚举项。如果未找到匹配项,会抛出 {@link ServiceException}。
|
||||
* </p>
|
||||
*
|
||||
* @param desc 描述,用于匹配对应的枚举项
|
||||
* @return TaskAssigneeEnum 返回对应的枚举类型
|
||||
* @throws ServiceException 如果未找到匹配的枚举项
|
||||
*/
|
||||
public static TaskAssigneeEnum fromDesc(String desc) {
|
||||
for (TaskAssigneeEnum type : values()) {
|
||||
if (type.getDesc().equals(desc)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new ServiceException("未知的办理人类型: " + desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码获取对应的枚举类型
|
||||
* <p>
|
||||
* 通过传入代码,查找并返回匹配的枚举项。如果未找到匹配项,会抛出 {@link ServiceException}。
|
||||
* </p>
|
||||
*
|
||||
* @param code 代码,用于匹配对应的枚举项
|
||||
* @return TaskAssigneeEnum 返回对应的枚举类型
|
||||
* @throws IllegalArgumentException 如果未找到匹配的枚举项
|
||||
*/
|
||||
public static TaskAssigneeEnum fromCode(String code) {
|
||||
for (TaskAssigneeEnum type : values()) {
|
||||
if (type.getCode().equals(code)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new ServiceException("未知的办理人类型代码: " + code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有办理人类型的描述列表,通常用于前端展示选择项。
|
||||
*
|
||||
* @return List<String> 返回所有办理人类型的描述列表
|
||||
*/
|
||||
public static List<String> getAssigneeTypeList() {
|
||||
return Arrays.stream(values())
|
||||
.map(TaskAssigneeEnum::getDesc)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有办理人类型的代码列表,通常用于程序内部逻辑判断。
|
||||
*
|
||||
* @return List<String> 返回所有办理人类型的代码列表
|
||||
*/
|
||||
public static List<String> getAssigneeCodeList() {
|
||||
return Arrays.stream(values())
|
||||
.map(TaskAssigneeEnum::getCode)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前办理人类型是否需要调用部门服务。
|
||||
*
|
||||
* @return 如果类型是 USER、DEPT 或 POST,则返回 true;否则返回 false
|
||||
*/
|
||||
public boolean needsDeptService() {
|
||||
return this == USER || this == DEPT || this == POST;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断给定字符串是否符合 SPEL 表达式格式(以 $ 或 # 开头)
|
||||
*
|
||||
* @param value 待判断字符串
|
||||
* @return 是否为 SPEL 表达式
|
||||
*/
|
||||
public static boolean isSpelExpression(String value) {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
// $前缀表示默认办理人变量策略
|
||||
// #前缀表示spel办理人变量策略
|
||||
return StringUtils.startsWith(value, "$") || StringUtils.startsWith(value, "#");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package org.dromara.workflow.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 人员类型
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum TaskAssigneeType {
|
||||
|
||||
/**
|
||||
* 待办任务的审批人权限
|
||||
* <p>该权限表示用户是待办任务的审批人,负责审核任务的执行情况。</p>
|
||||
*/
|
||||
APPROVER("1", "待办任务的审批人权限"),
|
||||
|
||||
/**
|
||||
* 待办任务的转办人权限
|
||||
* <p>该权限表示用户是待办任务的转办人,负责将任务分配给其他人员。</p>
|
||||
*/
|
||||
TRANSFER("2", "待办任务的转办人权限"),
|
||||
|
||||
/**
|
||||
* 待办任务的委托人权限
|
||||
* <p>该权限表示用户是待办任务的委托人,能够委托其他人代为处理任务。</p>
|
||||
*/
|
||||
DELEGATE("3", "待办任务的委托人权限"),
|
||||
|
||||
/**
|
||||
* 待办任务的抄送人权限
|
||||
* <p>该权限表示用户是待办任务的抄送人,仅接收任务信息的通知,不参与任务的审批或处理。</p>
|
||||
*/
|
||||
COPY("4", "待办任务的抄送人权限");
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package org.dromara.workflow.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 任务操作类型枚举,定义流程任务支持的运行时操作。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum TaskOperationEnum {
|
||||
|
||||
/**
|
||||
* 委派
|
||||
*/
|
||||
DELEGATE_TASK("delegateTask", "委派"),
|
||||
|
||||
/**
|
||||
* 转办
|
||||
*/
|
||||
TRANSFER_TASK("transferTask", "转办"),
|
||||
|
||||
/**
|
||||
* 加签
|
||||
*/
|
||||
ADD_SIGNATURE("addSignature", "加签"),
|
||||
|
||||
/**
|
||||
* 减签
|
||||
*/
|
||||
REDUCTION_SIGNATURE("reductionSignature", "减签");
|
||||
|
||||
/**
|
||||
* 操作编码,供接口与前端交互使用。
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* 操作描述。
|
||||
*/
|
||||
private final String desc;
|
||||
|
||||
/**
|
||||
* 操作编码缓存。
|
||||
*/
|
||||
private static final Map<String, TaskOperationEnum> CODE_MAP = Arrays.stream(values())
|
||||
.collect(Collectors.toConcurrentMap(TaskOperationEnum::getCode, Function.identity()));
|
||||
|
||||
/**
|
||||
* 根据 code 获取枚举实例。
|
||||
*
|
||||
* @param code 操作编码
|
||||
* @return 对应的枚举实例
|
||||
*/
|
||||
public static TaskOperationEnum getByCode(String code) {
|
||||
return CODE_MAP.get(code);
|
||||
}
|
||||
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
package org.dromara.workflow.common.enums;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 任务状态枚举
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum TaskStatusEnum {
|
||||
|
||||
/**
|
||||
* 撤销
|
||||
*/
|
||||
CANCEL("cancel", "撤销"),
|
||||
|
||||
/**
|
||||
* 通过
|
||||
*/
|
||||
PASS("pass", "通过"),
|
||||
|
||||
/**
|
||||
* 待审核
|
||||
*/
|
||||
WAITING("waiting", "待审核"),
|
||||
|
||||
/**
|
||||
* 作废
|
||||
*/
|
||||
INVALID("invalid", "作废"),
|
||||
|
||||
/**
|
||||
* 退回
|
||||
*/
|
||||
BACK("back", "退回"),
|
||||
|
||||
/**
|
||||
* 终止
|
||||
*/
|
||||
TERMINATION("termination", "终止"),
|
||||
|
||||
/**
|
||||
* 转办
|
||||
*/
|
||||
TRANSFER("transfer", "转办"),
|
||||
|
||||
/**
|
||||
* 委托
|
||||
*/
|
||||
DEPUTE("depute", "委托"),
|
||||
|
||||
/**
|
||||
* 抄送
|
||||
*/
|
||||
COPY("copy", "抄送"),
|
||||
|
||||
/**
|
||||
* 加签
|
||||
*/
|
||||
SIGN("sign", "加签"),
|
||||
|
||||
/**
|
||||
* 减签
|
||||
*/
|
||||
SIGN_OFF("sign_off", "减签"),
|
||||
|
||||
/**
|
||||
* 超时
|
||||
*/
|
||||
TIMEOUT("timeout", "超时");
|
||||
|
||||
/**
|
||||
* 状态编码与描述缓存。
|
||||
*/
|
||||
private static final Map<String, String> STATUS_DESC_MAP = Arrays.stream(values())
|
||||
.collect(Collectors.toConcurrentMap(TaskStatusEnum::getStatus, TaskStatusEnum::getDesc));
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private final String status;
|
||||
/**
|
||||
* 状态描述。
|
||||
*/
|
||||
private final String desc;
|
||||
|
||||
/**
|
||||
* 根据状态编码获取任务业务状态描述。
|
||||
*
|
||||
* @param status 状态
|
||||
* @return 状态描述
|
||||
*/
|
||||
public static String findByStatus(String status) {
|
||||
// 从缓存中直接获取描述
|
||||
return STATUS_DESC_MAP.getOrDefault(status, StrUtil.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断状态是否为通过或退回
|
||||
*
|
||||
* @param status 状态值
|
||||
* @return true 表示是通过或退回状态
|
||||
*/
|
||||
public static boolean isPassOrBack(String status) {
|
||||
return PASS.getStatus().equals(status) || BACK.getStatus().equals(status);
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package org.dromara.workflow.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 节点扩展变量枚举,占位用于后续补充可配置变量定义。
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum VariablesEnum implements NodeExtEnum {
|
||||
;
|
||||
|
||||
/**
|
||||
* 展示名称。
|
||||
*/
|
||||
private final String label;
|
||||
|
||||
/**
|
||||
* 变量值。
|
||||
*/
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* 是否选中。
|
||||
*/
|
||||
private final boolean selected;
|
||||
|
||||
}
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package org.dromara.workflow.config;
|
||||
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* WarmFlow 工作流配置入口,在工作流开关开启时注册相关组件。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Configuration
|
||||
public class WarmFlowConfig {
|
||||
|
||||
}
|
||||
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package org.dromara.workflow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.excel.utils.ExcelBuilder;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.redis.annotation.RepeatSubmit;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.common.constant.FlowConstant;
|
||||
import org.dromara.workflow.domain.bo.FlowCategoryBo;
|
||||
import org.dromara.workflow.domain.vo.FlowCategoryVo;
|
||||
import org.dromara.workflow.service.IFlwCategoryService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程分类
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/workflow/category")
|
||||
public class FlwCategoryController extends BaseController {
|
||||
|
||||
private final IFlwCategoryService flwCategoryService;
|
||||
|
||||
/**
|
||||
* 查询流程分类列表。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 流程分类列表
|
||||
*/
|
||||
@SaCheckPermission("workflow:category:list")
|
||||
@GetMapping("/list")
|
||||
public R<List<FlowCategoryVo>> list(FlowCategoryBo bo) {
|
||||
List<FlowCategoryVo> list = flwCategoryService.queryList(bo);
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出流程分类列表。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param response 响应流
|
||||
*/
|
||||
@SaCheckPermission("workflow:category:export")
|
||||
@Log(title = "流程分类", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(FlowCategoryBo bo, HttpServletResponse response) {
|
||||
List<FlowCategoryVo> list = flwCategoryService.queryList(bo);
|
||||
ExcelBuilder.of(list, FlowCategoryVo.class).sheetName("流程分类").toResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个流程分类详情。
|
||||
*
|
||||
* @param categoryId 主键
|
||||
* @return 流程分类详情
|
||||
*/
|
||||
@SaCheckPermission("workflow:category:query")
|
||||
@GetMapping("/{categoryId}")
|
||||
public R<FlowCategoryVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long categoryId) {
|
||||
return R.ok(flwCategoryService.queryById(categoryId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增流程分类,并校验分类名称唯一性。
|
||||
*
|
||||
* @param category 分类信息
|
||||
* @return 操作结果
|
||||
*/
|
||||
@SaCheckPermission("workflow:category:add")
|
||||
@Log(title = "流程分类", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody FlowCategoryBo category) {
|
||||
if (!flwCategoryService.checkCategoryNameUnique(category)) {
|
||||
return R.fail("新增流程分类'" + category.getCategoryName() + "'失败,流程分类名称已存在");
|
||||
}
|
||||
return toAjax(flwCategoryService.insertByBo(category));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改流程分类,并校验名称唯一及父子关系合法性。
|
||||
*
|
||||
* @param category 分类信息
|
||||
* @return 操作结果
|
||||
*/
|
||||
@SaCheckPermission("workflow:category:edit")
|
||||
@Log(title = "流程分类", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody FlowCategoryBo category) {
|
||||
Long categoryId = category.getCategoryId();
|
||||
if (!flwCategoryService.checkCategoryNameUnique(category)) {
|
||||
return R.fail("修改流程分类'" + category.getCategoryName() + "'失败,流程分类名称已存在");
|
||||
} else if (category.getParentId().equals(categoryId)) {
|
||||
return R.fail("修改流程分类'" + category.getCategoryName() + "'失败,上级流程分类不能是自己");
|
||||
}
|
||||
return toAjax(flwCategoryService.updateByBo(category));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除流程分类,删除前校验默认分类、子节点和绑定流程定义。
|
||||
*
|
||||
* @param categoryId 主键
|
||||
* @return 操作结果
|
||||
*/
|
||||
@SaCheckPermission("workflow:category:remove")
|
||||
@Log(title = "流程分类", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{categoryId}")
|
||||
public R<Void> remove(@PathVariable Long categoryId) {
|
||||
if (FlowConstant.FLOW_CATEGORY_ID.equals(categoryId)) {
|
||||
return R.warn("默认流程分类,不允许删除");
|
||||
}
|
||||
if (flwCategoryService.hasChildByCategoryId(categoryId)) {
|
||||
return R.warn("存在下级流程分类,不允许删除");
|
||||
}
|
||||
if (flwCategoryService.checkCategoryExistDefinition(categoryId)) {
|
||||
return R.warn("流程分类存在流程定义,不允许删除");
|
||||
}
|
||||
return toAjax(flwCategoryService.deleteWithValidById(categoryId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程分类树列表,用于流程定义选择分类节点。
|
||||
*
|
||||
* @param categoryBo 流程分类
|
||||
* @return 流程分类树
|
||||
*/
|
||||
@GetMapping("/categoryTree")
|
||||
public R<List<Tree<String>>> categoryTree(FlowCategoryBo categoryBo) {
|
||||
return R.ok(flwCategoryService.selectCategoryTreeList(categoryBo));
|
||||
}
|
||||
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
package org.dromara.workflow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.redis.annotation.RepeatSubmit;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.warm.flow.core.entity.Definition;
|
||||
import org.dromara.warm.flow.core.service.DefService;
|
||||
import org.dromara.warm.flow.orm.entity.FlowDefinition;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.vo.FlowDefinitionVo;
|
||||
import org.dromara.workflow.service.IFlwDefinitionService;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程定义管理 控制层
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/workflow/definition")
|
||||
public class FlwDefinitionController extends BaseController {
|
||||
|
||||
private final DefService defService;
|
||||
private final IFlwDefinitionService flwDefinitionService;
|
||||
|
||||
/**
|
||||
* 分页查询流程定义列表。
|
||||
*
|
||||
* @param flowDefinition 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 流程定义分页数据
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@SaCheckPermission("workflow:definition:list")
|
||||
public R<PageResult<FlowDefinitionVo>> list(FlowDefinition flowDefinition, PageQuery pageQuery) {
|
||||
return R.ok(flwDefinitionService.queryList(flowDefinition, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询未发布的流程定义列表。
|
||||
*
|
||||
* @param flowDefinition 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 未发布流程定义分页数据
|
||||
*/
|
||||
@GetMapping("/unPublishList")
|
||||
@SaCheckPermission("workflow:definition:list")
|
||||
public R<PageResult<FlowDefinitionVo>> unPublishList(FlowDefinition flowDefinition, PageQuery pageQuery) {
|
||||
return R.ok(flwDefinitionService.unPublishList(flowDefinition, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程定义详细信息。
|
||||
*
|
||||
* @param id 流程定义id
|
||||
* @return 流程定义详情
|
||||
*/
|
||||
@GetMapping(value = "/{id}")
|
||||
@SaCheckPermission("workflow:definition:query")
|
||||
public R<Definition> getInfo(@PathVariable Long id) {
|
||||
return R.ok(defService.getById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增流程定义并执行格式校验。
|
||||
*
|
||||
* @param flowDefinition 流程定义信息
|
||||
* @return 操作结果
|
||||
*/
|
||||
@Log(title = "流程定义", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
@RepeatSubmit()
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@SaCheckPermission("workflow:definition:add")
|
||||
public R<Boolean> add(@RequestBody FlowDefinition flowDefinition) {
|
||||
return R.ok(defService.checkAndSave(flowDefinition));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改流程定义。
|
||||
*
|
||||
* @param flowDefinition 流程定义信息
|
||||
* @return 操作结果
|
||||
*/
|
||||
@Log(title = "流程定义", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
@RepeatSubmit()
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@SaCheckPermission("workflow:definition:edit")
|
||||
public R<Boolean> edit(@RequestBody FlowDefinition flowDefinition) {
|
||||
return R.ok(defService.updateById(flowDefinition));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布流程定义,使其进入可用状态。
|
||||
*
|
||||
* @param id 流程定义id
|
||||
* @return 发布结果
|
||||
*/
|
||||
@Log(title = "流程定义", businessType = BusinessType.INSERT)
|
||||
@PutMapping("/publish/{id}")
|
||||
@RepeatSubmit()
|
||||
@SaCheckPermission("workflow:definition:publish")
|
||||
public R<Boolean> publish(@PathVariable Long id) {
|
||||
return R.ok(flwDefinitionService.publish(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消发布流程定义。
|
||||
*
|
||||
* @param id 流程定义id
|
||||
* @return 操作结果
|
||||
*/
|
||||
@Log(title = "流程定义", businessType = BusinessType.INSERT)
|
||||
@PutMapping("/unPublish/{id}")
|
||||
@RepeatSubmit()
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@SaCheckPermission("workflow:definition:publish")
|
||||
public R<Boolean> unPublish(@PathVariable Long id) {
|
||||
return R.ok(defService.unPublish(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除流程定义。
|
||||
*
|
||||
* @param ids 流程定义ID集合
|
||||
* @return 操作结果
|
||||
*/
|
||||
@Log(title = "流程定义", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
@SaCheckPermission("workflow:definition:remove")
|
||||
public R<Void> remove(@PathVariable List<Long> ids) {
|
||||
return toAjax(flwDefinitionService.removeDef(ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制一份流程定义,便于快速创建相似流程。
|
||||
*
|
||||
* @param id 流程定义id
|
||||
* @return 复制结果
|
||||
*/
|
||||
@Log(title = "流程定义", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/copy/{id}")
|
||||
@RepeatSubmit()
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@SaCheckPermission("workflow:definition:copy")
|
||||
public R<Boolean> copy(@PathVariable Long id) {
|
||||
return R.ok(defService.copyDef(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过文件导入流程定义。
|
||||
*
|
||||
* @param file 文件
|
||||
* @param category 分类
|
||||
* @return 导入结果
|
||||
*/
|
||||
@Log(title = "流程定义", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/importDef")
|
||||
@SaCheckPermission("workflow:definition:import")
|
||||
public R<Boolean> importDef(MultipartFile file, String category) {
|
||||
return R.ok(flwDefinitionService.importJson(file, category));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出流程定义文件。
|
||||
*
|
||||
* @param id 流程定义id
|
||||
* @param response 响应
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
@Log(title = "流程定义", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/exportDef/{id}")
|
||||
@SaCheckPermission("workflow:definition:export")
|
||||
public void exportDef(@PathVariable Long id, HttpServletResponse response) throws IOException {
|
||||
flwDefinitionService.exportDef(id, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程定义 JSON 字符串。
|
||||
*
|
||||
* @param id 流程定义id
|
||||
* @return 流程定义 JSON
|
||||
*/
|
||||
@GetMapping("/xmlString/{id}")
|
||||
@SaCheckPermission("workflow:definition:query")
|
||||
public R<String> xmlString(@PathVariable Long id) {
|
||||
return R.data(defService.exportJson(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活或挂起流程定义。
|
||||
*
|
||||
* @param id 流程定义id
|
||||
* @param active 激活/挂起
|
||||
* @return 处理结果
|
||||
*/
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/active/{id}")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Log(title = "流程定义", businessType = BusinessType.UPDATE)
|
||||
@SaCheckPermission("workflow:definition:active")
|
||||
public R<Boolean> active(@PathVariable Long id, @RequestParam boolean active) {
|
||||
return R.ok(active ? defService.active(id) : defService.unActive(id));
|
||||
}
|
||||
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
package org.dromara.workflow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.redis.annotation.RepeatSubmit;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.warm.flow.core.service.InsService;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.bo.FlowCancelBo;
|
||||
import org.dromara.workflow.domain.bo.FlowInstanceBo;
|
||||
import org.dromara.workflow.domain.bo.FlowInvalidBo;
|
||||
import org.dromara.workflow.domain.bo.FlowVariableBo;
|
||||
import org.dromara.workflow.domain.vo.FlowInstanceVo;
|
||||
import org.dromara.workflow.service.IFlwInstanceService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 流程实例管理 控制层
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/workflow/instance")
|
||||
public class FlwInstanceController extends BaseController {
|
||||
|
||||
private final InsService insService;
|
||||
private final IFlwInstanceService flwInstanceService;
|
||||
|
||||
/**
|
||||
* 分页查询正在运行的流程实例。
|
||||
*
|
||||
* @param flowInstanceBo 流程实例
|
||||
* @param pageQuery 分页
|
||||
* @return 正在运行的流程实例分页数据
|
||||
*/
|
||||
@GetMapping("/pageByRunning")
|
||||
@SaCheckPermission("workflow:instance:list")
|
||||
public R<PageResult<FlowInstanceVo>> selectRunningInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) {
|
||||
return R.ok(flwInstanceService.selectRunningInstanceList(flowInstanceBo, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询已结束的流程实例。
|
||||
*
|
||||
* @param flowInstanceBo 流程实例
|
||||
* @param pageQuery 分页
|
||||
* @return 已结束的流程实例分页数据
|
||||
*/
|
||||
@GetMapping("/pageByFinish")
|
||||
@SaCheckPermission("workflow:instance:list")
|
||||
public R<PageResult<FlowInstanceVo>> selectFinishInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) {
|
||||
return R.ok(flwInstanceService.selectFinishInstanceList(flowInstanceBo, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据业务 id 查询流程实例详细信息。
|
||||
*
|
||||
* @param businessId 业务id
|
||||
* @return 流程实例详情
|
||||
*/
|
||||
@GetMapping("/getInfo/{businessId}")
|
||||
@SaCheckPermission("workflow:instance:query")
|
||||
public R<FlowInstanceVo> getInfo(@PathVariable Long businessId) {
|
||||
return R.ok(flwInstanceService.queryByBusinessId(businessId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按业务 id 批量删除流程实例。
|
||||
*
|
||||
* @param businessIds 业务id
|
||||
* @return 操作结果
|
||||
*/
|
||||
@DeleteMapping("/deleteByBusinessIds/{businessIds}")
|
||||
@Log(title = "流程实例管理", businessType = BusinessType.DELETE)
|
||||
@SaCheckPermission("workflow:instance:remove")
|
||||
public R<Void> deleteByBusinessIds(@PathVariable List<Long> businessIds) {
|
||||
return toAjax(flwInstanceService.deleteByBusinessIds(StreamUtils.toList(businessIds, Convert::toStr)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按实例 id 批量删除流程实例。
|
||||
*
|
||||
* @param instanceIds 实例id
|
||||
* @return 操作结果
|
||||
*/
|
||||
@DeleteMapping("/deleteByInstanceIds/{instanceIds}")
|
||||
@Log(title = "流程实例管理", businessType = BusinessType.DELETE)
|
||||
@SaCheckPermission("workflow:instance:remove")
|
||||
public R<Void> deleteByInstanceIds(@PathVariable List<Long> instanceIds) {
|
||||
return toAjax(flwInstanceService.deleteByInstanceIds(instanceIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按实例 id 批量删除已完成的流程实例。
|
||||
*
|
||||
* @param instanceIds 实例id
|
||||
* @return 操作结果
|
||||
*/
|
||||
@DeleteMapping("/deleteHisByInstanceIds/{instanceIds}")
|
||||
@Log(title = "流程实例管理", businessType = BusinessType.DELETE)
|
||||
@SaCheckPermission("workflow:instance:remove")
|
||||
public R<Void> deleteHisByInstanceIds(@PathVariable List<Long> instanceIds) {
|
||||
return toAjax(flwInstanceService.deleteHisByInstanceIds(instanceIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销当前申请人发起的流程。
|
||||
*
|
||||
* @param bo 参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/cancelProcessApply")
|
||||
@Log(title = "流程实例管理", businessType = BusinessType.UPDATE)
|
||||
@SaCheckPermission("workflow:instance:cancel")
|
||||
public R<Void> cancelProcessApply(@RequestBody FlowCancelBo bo) {
|
||||
return toAjax(flwInstanceService.cancelProcessApply(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活或挂起流程实例。
|
||||
*
|
||||
* @param id 流程实例id
|
||||
* @param active 激活/挂起
|
||||
* @return 处理结果
|
||||
*/
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/active/{id}")
|
||||
@Log(title = "流程实例管理", businessType = BusinessType.UPDATE)
|
||||
@SaCheckPermission("workflow:instance:active")
|
||||
public R<Boolean> active(@PathVariable Long id, @RequestParam boolean active) {
|
||||
return R.ok(active ? insService.active(id) : insService.unActive(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录人发起的流程实例列表。
|
||||
*
|
||||
* @param flowInstanceBo 参数
|
||||
* @param pageQuery 分页
|
||||
* @return 当前用户发起的流程实例分页数据
|
||||
*/
|
||||
@GetMapping("/pageByCurrent")
|
||||
@SaCheckPermission("workflow:instance:currentList")
|
||||
public R<PageResult<FlowInstanceVo>> selectCurrentInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) {
|
||||
return R.ok(flwInstanceService.selectCurrentInstanceList(flowInstanceBo, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程图和流程记录,用于展示实例流转轨迹。
|
||||
*
|
||||
* @param businessId 业务id
|
||||
* @return 流程图与历史节点信息
|
||||
*/
|
||||
@GetMapping("/flowHisTaskList/{businessId}")
|
||||
@SaCheckPermission("workflow:instance:query")
|
||||
public R<Map<String, Object>> flowHisTaskList(@PathVariable String businessId) {
|
||||
return R.ok(flwInstanceService.flowHisTaskList(businessId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程变量。
|
||||
*
|
||||
* @param instanceId 流程实例id
|
||||
* @return 流程变量
|
||||
*/
|
||||
@GetMapping("/instanceVariable/{instanceId}")
|
||||
@SaCheckPermission("workflow:instance:variableQuery")
|
||||
public R<Map<String, Object>> instanceVariable(@PathVariable Long instanceId) {
|
||||
return R.ok(flwInstanceService.instanceVariable(instanceId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改流程变量。
|
||||
*
|
||||
* @param bo 参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/updateVariable")
|
||||
@Log(title = "流程实例管理", businessType = BusinessType.UPDATE)
|
||||
@SaCheckPermission("workflow:instance:variable")
|
||||
public R<Void> updateVariable(@Validated @RequestBody FlowVariableBo bo) {
|
||||
return toAjax(flwInstanceService.updateVariable(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 作废流程实例。
|
||||
*
|
||||
* @param bo 参数
|
||||
* @return 处理结果
|
||||
*/
|
||||
@Log(title = "流程实例管理", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/invalid")
|
||||
@SaCheckPermission("workflow:instance:invalid")
|
||||
public R<Boolean> invalid(@Validated @RequestBody FlowInvalidBo bo) {
|
||||
return R.ok(flwInstanceService.processInvalid(bo));
|
||||
}
|
||||
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package org.dromara.workflow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.redis.annotation.RepeatSubmit;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.bo.FlowSpelBo;
|
||||
import org.dromara.workflow.domain.vo.FlowSpelVo;
|
||||
import org.dromara.workflow.service.IFlwSpelService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程 SpEL 表达式定义控制器,负责表达式规则的增删改查。
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
* @date 2025-07-04
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/workflow/spel")
|
||||
public class FlwSpelController extends BaseController {
|
||||
|
||||
private final IFlwSpelService flwSpelService;
|
||||
|
||||
/**
|
||||
* 分页查询流程 SpEL 表达式定义列表。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 表达式分页数据
|
||||
*/
|
||||
@SaCheckPermission("workflow:spel:list")
|
||||
@GetMapping("/list")
|
||||
public R<PageResult<FlowSpelVo>> list(FlowSpelBo bo, PageQuery pageQuery) {
|
||||
return R.ok(flwSpelService.queryPageList(bo, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程 SpEL 表达式定义详情。
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 表达式详情
|
||||
*/
|
||||
@SaCheckPermission("workflow:spel:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<FlowSpelVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
|
||||
return R.ok(flwSpelService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增流程 SpEL 表达式定义。
|
||||
*
|
||||
* @param bo 表达式信息
|
||||
* @return 操作结果
|
||||
*/
|
||||
@SaCheckPermission("workflow:spel:add")
|
||||
@Log(title = "流程spel表达式定义", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody FlowSpelBo bo) {
|
||||
return toAjax(flwSpelService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改流程 SpEL 表达式定义。
|
||||
*
|
||||
* @param bo 表达式信息
|
||||
* @return 操作结果
|
||||
*/
|
||||
@SaCheckPermission("workflow:spel:edit")
|
||||
@Log(title = "流程spel表达式定义", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody FlowSpelBo bo) {
|
||||
return toAjax(flwSpelService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除流程 SpEL 表达式定义。
|
||||
*
|
||||
* @param ids 主键串
|
||||
* @return 操作结果
|
||||
*/
|
||||
@SaCheckPermission("workflow:spel:remove")
|
||||
@Log(title = "流程spel表达式定义", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
|
||||
return toAjax(flwSpelService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
}
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
package org.dromara.workflow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.redis.annotation.RepeatSubmit;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.system.api.domain.UserDTO;
|
||||
import org.dromara.warm.flow.core.entity.Node;
|
||||
import org.dromara.warm.flow.orm.entity.FlowNode;
|
||||
import org.dromara.workflow.api.domain.StartProcessReturnDTO;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.bo.*;
|
||||
import org.dromara.workflow.domain.vo.FlowHisTaskVo;
|
||||
import org.dromara.workflow.domain.vo.FlowTaskVo;
|
||||
import org.dromara.workflow.service.IFlwTaskService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 任务管理 控制层
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/workflow/task")
|
||||
public class FlwTaskController extends BaseController {
|
||||
|
||||
private final IFlwTaskService flwTaskService;
|
||||
|
||||
/**
|
||||
* 启动流程任务。
|
||||
*
|
||||
* @param startProcessBo 启动流程参数
|
||||
* @return 启动结果及后续流程信息
|
||||
*/
|
||||
@Log(title = "任务管理", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/startWorkFlow")
|
||||
public R<StartProcessReturnDTO> startWorkFlow(@Validated(AddGroup.class) @RequestBody StartProcessBo startProcessBo) {
|
||||
StartProcessReturnDTO startProcessReturn = flwTaskService.startWorkFlow(startProcessBo);
|
||||
return R.ok("提交成功", startProcessReturn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 办理当前任务节点。
|
||||
*
|
||||
* @param completeTaskBo 办理任务参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@Log(title = "任务管理", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/completeTask")
|
||||
public R<Void> completeTask(@Validated(AddGroup.class) @RequestBody CompleteTaskBo completeTaskBo) {
|
||||
return toAjax(flwTaskService.completeTask(completeTaskBo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户的待办任务。
|
||||
*
|
||||
* @param flowTaskBo 参数
|
||||
* @param pageQuery 分页
|
||||
* @return 待办任务分页数据
|
||||
*/
|
||||
@GetMapping("/pageByTaskWait")
|
||||
public R<PageResult<FlowTaskVo>> pageByTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
|
||||
return R.ok(flwTaskService.pageByTaskWait(flowTaskBo, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户的已办任务。
|
||||
*
|
||||
* @param flowTaskBo 参数
|
||||
* @param pageQuery 分页
|
||||
* @return 已办任务分页数据
|
||||
*/
|
||||
@GetMapping("/pageByTaskFinish")
|
||||
public R<PageResult<FlowHisTaskVo>> pageByTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
|
||||
return R.ok(flwTaskService.pageByTaskFinish(flowTaskBo, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询全部待办任务。
|
||||
*
|
||||
* @param flowTaskBo 参数
|
||||
* @param pageQuery 分页
|
||||
* @return 待办任务分页数据
|
||||
*/
|
||||
@SaCheckPermission("workflow:task:list")
|
||||
@GetMapping("/pageByAllTaskWait")
|
||||
public R<PageResult<FlowTaskVo>> pageByAllTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
|
||||
return R.ok(flwTaskService.pageByAllTaskWait(flowTaskBo, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询全部已办任务。
|
||||
*
|
||||
* @param flowTaskBo 参数
|
||||
* @param pageQuery 分页
|
||||
* @return 已办任务分页数据
|
||||
*/
|
||||
@SaCheckPermission("workflow:task:list")
|
||||
@GetMapping("/pageByAllTaskFinish")
|
||||
public R<PageResult<FlowHisTaskVo>> pageByAllTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
|
||||
return R.ok(flwTaskService.pageByAllTaskFinish(flowTaskBo, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户收到的抄送任务。
|
||||
*
|
||||
* @param flowTaskBo 参数
|
||||
* @param pageQuery 分页
|
||||
* @return 抄送任务分页数据
|
||||
*/
|
||||
@GetMapping("/pageByTaskCopy")
|
||||
public R<PageResult<FlowTaskVo>> pageByTaskCopy(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
|
||||
return R.ok(flwTaskService.pageByTaskCopy(flowTaskBo, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务 id 查询任务详情。
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @return 任务详情
|
||||
*/
|
||||
@GetMapping("/getTask/{taskId}")
|
||||
public R<FlowTaskVo> getTask(@PathVariable Long taskId) {
|
||||
return R.ok(flwTaskService.selectById(taskId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程下一节点信息。
|
||||
*
|
||||
* @param bo 参数
|
||||
* @return 下一节点列表
|
||||
*/
|
||||
@PostMapping("/getNextNodeList")
|
||||
public R<List<FlowNode>> getNextNodeList(@RequestBody FlowNextNodeBo bo) {
|
||||
return R.ok(flwTaskService.getNextNodeList(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 终止流程任务。
|
||||
*
|
||||
* @param bo 参数
|
||||
* @return 处理结果
|
||||
*/
|
||||
@Log(title = "任务管理", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/terminationTask")
|
||||
public R<Boolean> terminationTask(@RequestBody FlowTerminationBo bo) {
|
||||
return R.ok(flwTaskService.terminationTask(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务操作,例如委派、转办、加签或减签。
|
||||
*
|
||||
* @param bo 参数
|
||||
* @param taskOperation 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature
|
||||
* @return 操作结果
|
||||
*/
|
||||
@Log(title = "任务管理", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit
|
||||
@PostMapping("/taskOperation/{taskOperation}")
|
||||
public R<Void> taskOperation(@Validated @RequestBody TaskOperationBo bo, @PathVariable String taskOperation) {
|
||||
return toAjax(flwTaskService.taskOperation(bo, taskOperation));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量修改任务办理人。
|
||||
*
|
||||
* @param taskIdList 任务id
|
||||
* @param userId 办理人id
|
||||
* @return 操作结果
|
||||
*/
|
||||
@Log(title = "任务管理", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@SaCheckPermission("workflow:task:edit")
|
||||
@PutMapping("/updateAssignee/{userId}")
|
||||
public R<Void> updateAssignee(@RequestBody List<Long> taskIdList, @PathVariable String userId) {
|
||||
return toAjax(flwTaskService.updateAssignee(taskIdList, userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 驳回审批到前置节点。
|
||||
*
|
||||
* @param bo 参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@Log(title = "任务管理", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/backProcess")
|
||||
public R<Void> backProcess(@Validated({AddGroup.class}) @RequestBody BackProcessBo bo) {
|
||||
return toAjax(flwTaskService.backProcess(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前任务可驳回的前置节点。
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @param nowNodeCode 当前节点
|
||||
* @return 可驳回节点列表
|
||||
*/
|
||||
@GetMapping("/getBackTaskNode/{taskId}/{nowNodeCode}")
|
||||
public R<List<Node>> getBackTaskNode(@PathVariable Long taskId, @PathVariable String nowNodeCode) {
|
||||
return R.ok(flwTaskService.getBackTaskNode(taskId, nowNodeCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前任务的所有办理人。
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @return 办理人列表
|
||||
*/
|
||||
@GetMapping("/currentTaskAllUser/{taskId}")
|
||||
public R<List<UserDTO>> currentTaskAllUser(@PathVariable Long taskId) {
|
||||
return R.ok(flwTaskService.currentTaskAllUser(List.of(taskId)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 催办任务。
|
||||
*
|
||||
* @param bo 参数
|
||||
* @return 结果
|
||||
*/
|
||||
@PostMapping("/urgeTask")
|
||||
@Log(title = "任务管理", businessType = BusinessType.INSERT)
|
||||
@SaCheckPermission("workflow:task:edit")
|
||||
public R<Void> urgeTask(@RequestBody FlowUrgeTaskBo bo) {
|
||||
return toAjax(flwTaskService.urgeTask(bo));
|
||||
}
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
package org.dromara.workflow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.excel.utils.ExcelBuilder;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.redis.annotation.RepeatSubmit;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.bo.TestLeaveBo;
|
||||
import org.dromara.workflow.domain.vo.TestLeaveVo;
|
||||
import org.dromara.workflow.service.ITestLeaveService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 请假示例控制器,演示业务单据与流程引擎联动的典型用法。
|
||||
*
|
||||
* @author may
|
||||
* @date 2023-07-21
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/workflow/leave")
|
||||
public class TestLeaveController extends BaseController {
|
||||
|
||||
private final ITestLeaveService testLeaveService;
|
||||
|
||||
/**
|
||||
* 分页查询请假列表。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 请假分页数据
|
||||
*/
|
||||
@SaCheckPermission("workflow:leave:list")
|
||||
@GetMapping("/list")
|
||||
public R<PageResult<TestLeaveVo>> list(TestLeaveBo bo, PageQuery pageQuery) {
|
||||
return R.ok(testLeaveService.queryPageList(bo, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出请假列表。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param response 响应流
|
||||
*/
|
||||
@SaCheckPermission("workflow:leave:export")
|
||||
@Log(title = "请假", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(TestLeaveBo bo, HttpServletResponse response) {
|
||||
List<TestLeaveVo> list = testLeaveService.queryList(bo);
|
||||
ExcelBuilder.of(list, TestLeaveVo.class).sheetName("请假").toResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请假单详情。
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 请假详情
|
||||
*/
|
||||
@SaCheckPermission("workflow:leave:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<TestLeaveVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(testLeaveService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增请假单。
|
||||
*
|
||||
* @param bo 请假信息
|
||||
* @return 新增后的请假单
|
||||
*/
|
||||
@SaCheckPermission("workflow:leave:add")
|
||||
@Log(title = "请假", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<TestLeaveVo> add(@Validated(AddGroup.class) @RequestBody TestLeaveBo bo) {
|
||||
return R.ok(testLeaveService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交请假单并同步发起流程。
|
||||
*
|
||||
* @param bo 请假信息
|
||||
* @return 提交后的请假单
|
||||
*/
|
||||
@SaCheckPermission("workflow:leave:add")
|
||||
@Log(title = "请假", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/submitAndFlowStart")
|
||||
public R<TestLeaveVo> submitAndFlowStart(@Validated(AddGroup.class) @RequestBody TestLeaveBo bo) {
|
||||
return R.ok(testLeaveService.submitAndFlowStart(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改请假单。
|
||||
*
|
||||
* @param bo 请假信息
|
||||
* @return 修改后的请假单
|
||||
*/
|
||||
@SaCheckPermission("workflow:leave:edit")
|
||||
@Log(title = "请假", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<TestLeaveVo> edit(@Validated(EditGroup.class) @RequestBody TestLeaveBo bo) {
|
||||
return R.ok(testLeaveService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除请假单。
|
||||
*
|
||||
* @param ids 主键串
|
||||
* @return 操作结果
|
||||
*/
|
||||
@SaCheckPermission("workflow:leave:remove")
|
||||
@Log(title = "请假", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(testLeaveService.deleteWithValidByIds(List.of(ids)));
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package org.dromara.workflow.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程分类对象 wf_category
|
||||
*
|
||||
* @author may
|
||||
* @date 2023-06-27
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("flow_category")
|
||||
public class FlowCategory extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 流程分类ID
|
||||
*/
|
||||
@TableId(value = "category_id")
|
||||
private Long categoryId;
|
||||
|
||||
/**
|
||||
* 父流程分类id
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 祖级列表
|
||||
*/
|
||||
private String ancestors;
|
||||
|
||||
/**
|
||||
* 流程分类名称
|
||||
*/
|
||||
private String categoryName;
|
||||
|
||||
/**
|
||||
* 显示顺序
|
||||
*/
|
||||
private Long orderNum;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 子菜单
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<FlowCategory> children = new ArrayList<>();
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package org.dromara.workflow.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 流程实例业务扩展对象 flow_instance_biz_ext
|
||||
*
|
||||
* @author may
|
||||
* @date 2025-08-05
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("flow_instance_biz_ext")
|
||||
public class FlowInstanceBizExt extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 流程实例ID
|
||||
*/
|
||||
private Long instanceId;
|
||||
|
||||
/**
|
||||
* 业务ID
|
||||
*/
|
||||
private String businessId;
|
||||
|
||||
/**
|
||||
* 业务编码
|
||||
*/
|
||||
private String businessCode;
|
||||
|
||||
/**
|
||||
* 业务标题
|
||||
*/
|
||||
private String businessTitle;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private String delFlag;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package org.dromara.workflow.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 流程spel表达式定义对象 flow_spel
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
* @date 2025-07-04
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("flow_spel")
|
||||
public class FlowSpel extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 组件名称
|
||||
*/
|
||||
private String componentName;
|
||||
|
||||
/**
|
||||
* 方法名
|
||||
*/
|
||||
private String methodName;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private String methodParams;
|
||||
|
||||
/**
|
||||
* 预览spel表达式
|
||||
*/
|
||||
private String viewSpel;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 删除标志
|
||||
*/
|
||||
@TableLogic
|
||||
private String delFlag;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.dromara.workflow.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 请假对象 test_leave
|
||||
*
|
||||
* @author may
|
||||
* @date 2023-07-21
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("test_leave")
|
||||
public class TestLeave extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 申请编号
|
||||
*/
|
||||
private String applyCode;
|
||||
|
||||
/**
|
||||
* 请假类型
|
||||
*/
|
||||
private String leaveType;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private LocalDateTime startDate;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private LocalDateTime endDate;
|
||||
|
||||
/**
|
||||
* 请假天数
|
||||
*/
|
||||
private Integer leaveDays;
|
||||
|
||||
/**
|
||||
* 请假原因
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/**
|
||||
* 驳回参数请求
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class BackProcessBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 任务ID
|
||||
*/
|
||||
@NotNull(message = "任务ID不能为空", groups = AddGroup.class)
|
||||
private Long taskId;
|
||||
|
||||
/**
|
||||
* 附件id
|
||||
*/
|
||||
private String fileId;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private List<String> messageType;
|
||||
|
||||
/**
|
||||
* 驳回的节点id(目前未使用,直接驳回到申请人)
|
||||
*/
|
||||
private String nodeCode;
|
||||
|
||||
/**
|
||||
* 办理意见
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 通知
|
||||
*/
|
||||
private String notice;
|
||||
|
||||
/**
|
||||
* 流程变量
|
||||
*/
|
||||
private Map<String, Object> variables;
|
||||
|
||||
public Map<String, Object> getVariables() {
|
||||
if (variables == null) {
|
||||
variables = new HashMap<>(16);
|
||||
return variables;
|
||||
}
|
||||
variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
|
||||
return variables;
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 办理任务请求对象
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class CompleteTaskBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 任务id
|
||||
*/
|
||||
@NotNull(message = "任务id不能为空", groups = {AddGroup.class})
|
||||
private Long taskId;
|
||||
|
||||
/**
|
||||
* 附件id
|
||||
*/
|
||||
private String fileId;
|
||||
|
||||
/**
|
||||
* 抄送人员
|
||||
*/
|
||||
private List<FlowCopyBo> flowCopyList;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private List<String> messageType;
|
||||
|
||||
/**
|
||||
* 办理意见
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 消息通知
|
||||
*/
|
||||
private String notice;
|
||||
|
||||
/**
|
||||
* 办理人(可不填 用于覆盖当前节点办理人)
|
||||
*/
|
||||
private String handler;
|
||||
|
||||
/**
|
||||
* 流程变量
|
||||
*/
|
||||
private Map<String, Object> variables;
|
||||
|
||||
/**
|
||||
* 弹窗选择的办理人
|
||||
*/
|
||||
private Map<String, Object> assigneeMap;
|
||||
|
||||
/**
|
||||
* 扩展变量(此处为逗号分隔的ossId)
|
||||
*/
|
||||
private String ext;
|
||||
|
||||
public Map<String, Object> getVariables() {
|
||||
if (variables == null) {
|
||||
variables = new HashMap<>(16);
|
||||
return variables;
|
||||
}
|
||||
variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
|
||||
return variables;
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
|
||||
/**
|
||||
* 撤销流程请求对象。
|
||||
*
|
||||
* @param businessId 业务 ID
|
||||
* @param message 撤销说明
|
||||
* @author may
|
||||
*/
|
||||
public record FlowCancelBo(
|
||||
@NotBlank(message = "业务ID不能为空", groups = AddGroup.class)
|
||||
String businessId,
|
||||
String message
|
||||
) {
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.workflow.domain.FlowCategory;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 流程分类业务对象 wf_category
|
||||
*
|
||||
* @author may
|
||||
* @date 2023-06-27
|
||||
*/
|
||||
@Data
|
||||
@AutoMapper(target = FlowCategory.class, reverseConvertGenerate = false)
|
||||
public class FlowCategoryBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 流程分类ID
|
||||
*/
|
||||
@NotNull(message = "流程分类ID不能为空", groups = {EditGroup.class})
|
||||
private Long categoryId;
|
||||
|
||||
/**
|
||||
* 父流程分类id
|
||||
*/
|
||||
@NotNull(message = "父流程分类id不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 流程分类名称
|
||||
*/
|
||||
@NotBlank(message = "流程分类名称不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private String categoryName;
|
||||
|
||||
/**
|
||||
* 显示顺序
|
||||
*/
|
||||
private Long orderNum;
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* 流程抄送请求对象。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class FlowCopyBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
private String nickName;
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程实例查询条件对象。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class FlowInstanceBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 流程定义名称
|
||||
*/
|
||||
private String flowName;
|
||||
|
||||
/**
|
||||
* 流程定义编码
|
||||
*/
|
||||
private String flowCode;
|
||||
|
||||
/**
|
||||
* 任务发起人
|
||||
*/
|
||||
private String startUserId;
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
private String businessId;
|
||||
|
||||
/**
|
||||
* 流程分类id
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 任务名称
|
||||
*/
|
||||
private String nodeName;
|
||||
|
||||
/**
|
||||
* 申请人Ids
|
||||
*/
|
||||
private List<String> createByIds;
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
|
||||
/**
|
||||
* 作废流程请求对象。
|
||||
*
|
||||
* @param id 流程实例 ID
|
||||
* @param comment 作废意见
|
||||
* @author may
|
||||
*/
|
||||
public record FlowInvalidBo(
|
||||
@NotNull(message = "流程实例id为空", groups = AddGroup.class)
|
||||
Long id,
|
||||
String comment
|
||||
) {
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 查询下一节点信息的请求对象。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class FlowNextNodeBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
/**
|
||||
* 任务id
|
||||
*/
|
||||
private Long taskId;
|
||||
|
||||
/**
|
||||
* 流程变量
|
||||
*/
|
||||
private Map<String, Object> variables;
|
||||
|
||||
/**
|
||||
* 获取流程变量并自动剔除空值。
|
||||
*
|
||||
* @return 流程变量
|
||||
*/
|
||||
public Map<String, Object> getVariables() {
|
||||
if (variables == null) {
|
||||
variables = new HashMap<>(16);
|
||||
return variables;
|
||||
}
|
||||
variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
|
||||
return variables;
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.workflow.domain.FlowSpel;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 流程spel表达式定义业务对象 flow_spel
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
* @date 2025-07-04
|
||||
*/
|
||||
@Data
|
||||
@AutoMapper(target = FlowSpel.class, reverseConvertGenerate = false)
|
||||
public class FlowSpelBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 组件名称
|
||||
*/
|
||||
private String componentName;
|
||||
|
||||
/**
|
||||
* 方法名
|
||||
*/
|
||||
private String methodName;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private String methodParams;
|
||||
|
||||
/**
|
||||
* 预览spel值
|
||||
*/
|
||||
@NotBlank(message = "预览spel值不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private String viewSpel;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
@NotBlank(message = "状态(0正常 1停用)不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
private Map<String, Object> params = new HashMap<>();
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 任务请求对象
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class FlowTaskBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 任务名称
|
||||
*/
|
||||
private String nodeName;
|
||||
|
||||
/**
|
||||
* 流程定义名称
|
||||
*/
|
||||
private String flowName;
|
||||
|
||||
/**
|
||||
* 流程定义编码
|
||||
*/
|
||||
private String flowCode;
|
||||
|
||||
/**
|
||||
* 流程分类id
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 流程实例id
|
||||
*/
|
||||
private Long instanceId;
|
||||
|
||||
/**
|
||||
* 流程状态
|
||||
*/
|
||||
private String flowStatus;
|
||||
|
||||
/**
|
||||
* 权限列表
|
||||
*/
|
||||
private List<String> permissionList;
|
||||
|
||||
/**
|
||||
* 申请人Ids
|
||||
*/
|
||||
private List<Long> createByIds;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private Map<String, Object> params = new HashMap<>();
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
|
||||
/**
|
||||
* 终止任务请求对象
|
||||
*
|
||||
* @param taskId 任务 ID
|
||||
* @param comment 终止意见
|
||||
* @author may
|
||||
*/
|
||||
public record FlowTerminationBo(
|
||||
@NotNull(message = "任务id为空", groups = AddGroup.class)
|
||||
Long taskId,
|
||||
String comment
|
||||
) {
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程变量参数
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class FlowUrgeTaskBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 任务id
|
||||
*/
|
||||
@NotNull(message = "任务id为空", groups = AddGroup.class)
|
||||
private List<Long> taskIdList;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private List<String> messageType;
|
||||
|
||||
/**
|
||||
* 催办内容
|
||||
*/
|
||||
@NotNull(message = "催办内容为空", groups = AddGroup.class)
|
||||
private String message;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
|
||||
/**
|
||||
* 流程变量参数
|
||||
*
|
||||
* @param instanceId 流程实例 ID
|
||||
* @param key 变量键
|
||||
* @param value 变量值
|
||||
* @author may
|
||||
*/
|
||||
public record FlowVariableBo(
|
||||
@NotNull(message = "流程实例id为空", groups = AddGroup.class)
|
||||
Long instanceId,
|
||||
@NotNull(message = "流程变量key为空", groups = AddGroup.class)
|
||||
String key,
|
||||
@NotNull(message = "流程变量value为空", groups = AddGroup.class)
|
||||
String value
|
||||
) {
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.workflow.domain.FlowInstanceBizExt;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 启动流程对象
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class StartProcessBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 业务唯一值id
|
||||
*/
|
||||
@NotBlank(message = "业务ID不能为空", groups = {AddGroup.class})
|
||||
private String businessId;
|
||||
|
||||
/**
|
||||
* 流程定义编码
|
||||
*/
|
||||
@NotBlank(message = "流程定义编码不能为空", groups = {AddGroup.class})
|
||||
private String flowCode;
|
||||
|
||||
/**
|
||||
* 办理人(可不填 用于覆盖当前节点办理人)
|
||||
*/
|
||||
private String handler;
|
||||
|
||||
/**
|
||||
* 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}}
|
||||
*/
|
||||
private Map<String, Object> variables;
|
||||
|
||||
/**
|
||||
* 流程业务扩展信息
|
||||
*/
|
||||
private FlowInstanceBizExt bizExt;
|
||||
|
||||
public Map<String, Object> getVariables() {
|
||||
if (variables == null) {
|
||||
variables = new HashMap<>(16);
|
||||
return variables;
|
||||
}
|
||||
variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
|
||||
return variables;
|
||||
}
|
||||
|
||||
public FlowInstanceBizExt getBizExt() {
|
||||
if (ObjectUtil.isNull(bizExt)) {
|
||||
bizExt = new FlowInstanceBizExt();
|
||||
}
|
||||
return bizExt;
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 任务操作业务对象,用于描述任务委派、转办、加签等操作的必要参数
|
||||
* 包含了用户ID、任务ID、任务相关的消息、以及加签/减签的用户ID
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@Data
|
||||
public class TaskOperationBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 委派/转办人的用户ID(必填,准对委派/转办人操作)
|
||||
*/
|
||||
@NotNull(message = "委派/转办人id不能为空", groups = {AddGroup.class})
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 加签/减签人的用户ID列表(必填,针对加签/减签操作)
|
||||
*/
|
||||
@NotNull(message = "加签/减签id不能为空", groups = {EditGroup.class})
|
||||
private List<String> userIds;
|
||||
|
||||
/**
|
||||
* 任务ID(必填)
|
||||
*/
|
||||
@NotNull(message = "任务id不能为空")
|
||||
private Long taskId;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private List<String> messageType;
|
||||
|
||||
/**
|
||||
* 意见或备注信息(可选)
|
||||
*/
|
||||
private String message;
|
||||
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.workflow.domain.TestLeave;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 请假业务对象 test_leave
|
||||
*
|
||||
* @author may
|
||||
* @date 2023-07-21
|
||||
*/
|
||||
@Data
|
||||
@AutoMapper(target = TestLeave.class, reverseConvertGenerate = false)
|
||||
public class TestLeaveBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@NotNull(message = "主键不能为空", groups = {EditGroup.class})
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 流程code
|
||||
*/
|
||||
private String flowCode;
|
||||
|
||||
/**
|
||||
* 申请编号
|
||||
*/
|
||||
private String applyCode;
|
||||
|
||||
/**
|
||||
* 请假类型
|
||||
*/
|
||||
@NotBlank(message = "请假类型不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private String leaveType;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
@NotNull(message = "开始时间不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDateTime startDate;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
@NotNull(message = "结束时间不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDateTime endDate;
|
||||
|
||||
/**
|
||||
* 请假天数
|
||||
*/
|
||||
private Integer leaveDays;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private Integer startLeaveDays;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private Integer endLeaveDays;
|
||||
|
||||
/**
|
||||
* 请假原因
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
private Map<String, Object> params = new HashMap<>();
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package org.dromara.workflow.domain.context;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.warm.flow.core.dto.FlowParams;
|
||||
import org.dromara.warm.flow.core.entity.Instance;
|
||||
import org.dromara.warm.flow.orm.entity.FlowTask;
|
||||
import org.dromara.workflow.domain.bo.CompleteTaskBo;
|
||||
|
||||
/**
|
||||
* 办理任务 LiteFlow 上下文。
|
||||
* <p>
|
||||
* 链路内各节点通过该对象传递任务、实例、办理参数等中间结果,避免组件之间重复查询。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class CompleteTaskContext {
|
||||
|
||||
/**
|
||||
* 办理任务入参。
|
||||
*/
|
||||
private final CompleteTaskBo completeTaskBo;
|
||||
|
||||
/**
|
||||
* 当前待办理的运行任务,由校验节点加载。
|
||||
*/
|
||||
private FlowTask flowTask;
|
||||
|
||||
/**
|
||||
* 当前任务所属流程实例,由校验节点加载。
|
||||
*/
|
||||
private Instance instance;
|
||||
|
||||
/**
|
||||
* Warm-Flow 办理参数,由参数构建节点生成并供执行、自动审批节点复用。
|
||||
*/
|
||||
private FlowParams flowParams;
|
||||
|
||||
/**
|
||||
* 流程实例是否开启自动审批,由参数构建节点从实例变量中解析。
|
||||
*/
|
||||
private Boolean autoPass;
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package org.dromara.workflow.domain.context;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.warm.flow.orm.entity.FlowInstance;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 删除流程实例 LiteFlow 上下文。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class InstanceDeleteContext {
|
||||
|
||||
/**
|
||||
* 按业务 id 删除时的业务 id 集合。
|
||||
*/
|
||||
private final List<String> businessIds;
|
||||
|
||||
/**
|
||||
* 按实例 id 删除时的实例 id 集合。
|
||||
*/
|
||||
private final Collection<Long> instanceIds;
|
||||
|
||||
/**
|
||||
* 是否删除历史实例及其历史任务数据。
|
||||
*/
|
||||
private final boolean history;
|
||||
|
||||
/**
|
||||
* 待删除流程实例。
|
||||
*/
|
||||
private List<FlowInstance> flowInstances;
|
||||
|
||||
/**
|
||||
* 实际执行删除的实例 id。
|
||||
*/
|
||||
private List<Long> deleteInstanceIds;
|
||||
|
||||
/**
|
||||
* 删除结果。
|
||||
*/
|
||||
private boolean result;
|
||||
|
||||
public static InstanceDeleteContext byBusinessIds(List<String> businessIds) {
|
||||
return new InstanceDeleteContext(businessIds, null, false);
|
||||
}
|
||||
|
||||
public static InstanceDeleteContext byInstanceIds(Collection<Long> instanceIds) {
|
||||
return new InstanceDeleteContext(null, instanceIds, false);
|
||||
}
|
||||
|
||||
public static InstanceDeleteContext byHistoryInstanceIds(Collection<Long> instanceIds) {
|
||||
return new InstanceDeleteContext(null, instanceIds, true);
|
||||
}
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package org.dromara.workflow.domain.context;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.warm.flow.core.entity.Definition;
|
||||
import org.dromara.warm.flow.core.entity.Instance;
|
||||
import org.dromara.warm.flow.core.entity.Task;
|
||||
import org.dromara.warm.flow.orm.entity.FlowInstance;
|
||||
import org.dromara.workflow.api.domain.StartProcessReturnDTO;
|
||||
import org.dromara.workflow.domain.FlowInstanceBizExt;
|
||||
import org.dromara.workflow.domain.bo.StartProcessBo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 启动流程 LiteFlow 上下文。
|
||||
* <p>
|
||||
* 链路内保存启动参数、流程定义、流程实例和首个任务等中间结果。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class StartProcessContext {
|
||||
|
||||
/**
|
||||
* 启动流程入参。
|
||||
*/
|
||||
private final StartProcessBo startProcessBo;
|
||||
|
||||
/**
|
||||
* 业务唯一标识。
|
||||
*/
|
||||
private String businessId;
|
||||
|
||||
/**
|
||||
* 流程变量。
|
||||
*/
|
||||
private Map<String, Object> variables;
|
||||
|
||||
/**
|
||||
* 流程实例业务扩展信息。
|
||||
*/
|
||||
private FlowInstanceBizExt bizExt;
|
||||
|
||||
/**
|
||||
* 已存在的流程实例,非空时走续提交逻辑。
|
||||
*/
|
||||
private FlowInstance existingInstance;
|
||||
|
||||
/**
|
||||
* 已发布的流程定义。
|
||||
*/
|
||||
private Definition definition;
|
||||
|
||||
/**
|
||||
* 新启动的流程实例。
|
||||
*/
|
||||
private Instance instance;
|
||||
|
||||
/**
|
||||
* 当前流程实例下生成的任务列表。
|
||||
*/
|
||||
private List<Task> taskList;
|
||||
|
||||
/**
|
||||
* 启动流程返回结果。
|
||||
*/
|
||||
private StartProcessReturnDTO startProcessReturn;
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package org.dromara.workflow.domain.context;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.warm.flow.core.dto.FlowParams;
|
||||
import org.dromara.warm.flow.core.entity.Task;
|
||||
import org.dromara.warm.flow.orm.entity.FlowNode;
|
||||
import org.dromara.workflow.common.enums.TaskOperationEnum;
|
||||
import org.dromara.workflow.domain.bo.TaskOperationBo;
|
||||
|
||||
/**
|
||||
* 任务操作 LiteFlow 上下文。
|
||||
* <p>
|
||||
* 用于在委派、转办、加签、减签链路中传递操作类型、任务、节点和办理参数。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class TaskOperationContext {
|
||||
|
||||
/**
|
||||
* 任务操作入参。
|
||||
*/
|
||||
private final TaskOperationBo taskOperationBo;
|
||||
|
||||
/**
|
||||
* 操作类型编码。
|
||||
*/
|
||||
private final String taskOperation;
|
||||
|
||||
/**
|
||||
* 解析后的操作枚举。
|
||||
*/
|
||||
private TaskOperationEnum operation;
|
||||
|
||||
/**
|
||||
* Warm-Flow 操作参数。
|
||||
*/
|
||||
private FlowParams flowParams;
|
||||
|
||||
/**
|
||||
* 当前任务。
|
||||
*/
|
||||
private Task task;
|
||||
|
||||
/**
|
||||
* 当前任务对应的流程节点。
|
||||
*/
|
||||
private FlowNode flowNode;
|
||||
|
||||
/**
|
||||
* 操作执行结果。
|
||||
*/
|
||||
private boolean result;
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package org.dromara.workflow.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 按钮权限视图对象。
|
||||
*
|
||||
* @author may
|
||||
* @date 2025-02-28
|
||||
*/
|
||||
@Data
|
||||
public class ButtonPermissionVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 唯一编码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 选项值
|
||||
*/
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* 是否显示
|
||||
*/
|
||||
private Boolean show;
|
||||
|
||||
/**
|
||||
* 无参构造方法。
|
||||
*/
|
||||
public ButtonPermissionVo() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用按钮编码和显示状态构造按钮权限对象。
|
||||
*
|
||||
* @param code 按钮编码
|
||||
* @param show 是否显示
|
||||
*/
|
||||
public ButtonPermissionVo(String code, Boolean show) {
|
||||
this.code = code;
|
||||
this.show = show;
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package org.dromara.workflow.domain.vo;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import org.dromara.common.translation.annotation.Translation;
|
||||
import org.dromara.workflow.common.constant.FlowConstant;
|
||||
import org.dromara.workflow.domain.FlowCategory;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 流程分类视图对象 wf_category
|
||||
*
|
||||
* @author may
|
||||
* @date 2023-06-27
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = FlowCategory.class)
|
||||
public class FlowCategoryVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 流程分类ID
|
||||
*/
|
||||
@ExcelProperty(value = "流程分类ID")
|
||||
private Long categoryId;
|
||||
|
||||
/**
|
||||
* 父级分类id
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 父级分类名称
|
||||
*/
|
||||
@Translation(type = FlowConstant.CATEGORY_ID_TO_NAME, mapper = "parentId")
|
||||
private String parentName;
|
||||
|
||||
/**
|
||||
* 祖级列表
|
||||
*/
|
||||
private String ancestors;
|
||||
|
||||
/**
|
||||
* 流程分类名称
|
||||
*/
|
||||
@ExcelProperty(value = "流程分类名称")
|
||||
private String categoryName;
|
||||
|
||||
/**
|
||||
* 显示顺序
|
||||
*/
|
||||
@ExcelProperty(value = "显示顺序")
|
||||
private Long orderNum;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@ExcelProperty(value = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package org.dromara.workflow.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.common.translation.annotation.Translation;
|
||||
import org.dromara.common.translation.constant.TransConstant;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 流程抄送视图对象。
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@Data
|
||||
public class FlowCopyVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "userId")
|
||||
private String nickName;
|
||||
|
||||
/**
|
||||
* 使用用户 ID 构造抄送对象。
|
||||
*
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
public FlowCopyVo(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package org.dromara.workflow.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.common.translation.annotation.Translation;
|
||||
import org.dromara.workflow.common.constant.FlowConstant;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 流程定义视图
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class FlowDefinitionVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 流程定义主键
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 流程定义编码
|
||||
*/
|
||||
private String flowCode;
|
||||
|
||||
/**
|
||||
* 流程定义名称
|
||||
*/
|
||||
private String flowName;
|
||||
|
||||
/**
|
||||
* 流程分类id
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 流程分类名称
|
||||
*/
|
||||
@Translation(type = FlowConstant.CATEGORY_ID_TO_NAME, mapper = "category")
|
||||
private String categoryName;
|
||||
|
||||
/**
|
||||
* 流程版本
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 是否发布(0未发布 1已发布 9失效)
|
||||
*/
|
||||
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;
|
||||
}
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
package org.dromara.workflow.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.utils.DateUtils;
|
||||
import org.dromara.common.translation.annotation.Translation;
|
||||
import org.dromara.common.translation.constant.TransConstant;
|
||||
import org.dromara.warm.flow.core.enums.CooperateType;
|
||||
import org.dromara.workflow.common.constant.FlowConstant;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 历史任务视图
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class FlowHisTaskVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 历史任务主键
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 对应flow_definition表的id
|
||||
*/
|
||||
private Long definitionId;
|
||||
|
||||
/**
|
||||
* 流程定义名称
|
||||
*/
|
||||
private String flowName;
|
||||
|
||||
/**
|
||||
* 流程实例表id
|
||||
*/
|
||||
private Long instanceId;
|
||||
|
||||
/**
|
||||
* 任务表id
|
||||
*/
|
||||
private Long taskId;
|
||||
|
||||
/**
|
||||
* 协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)
|
||||
*/
|
||||
private Integer cooperateType;
|
||||
|
||||
/**
|
||||
* 协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)
|
||||
*/
|
||||
private String cooperateTypeName;
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* 审批者
|
||||
*/
|
||||
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "approver")
|
||||
private String approverName;
|
||||
|
||||
/**
|
||||
* 协作人(只有转办、会签、票签、委派)
|
||||
*/
|
||||
private String collaborator;
|
||||
|
||||
/**
|
||||
* 权限标识 permissionFlag的list形式
|
||||
*/
|
||||
private List<String> permissionList;
|
||||
|
||||
/**
|
||||
* 跳转类型(PASS通过 REJECT退回 NONE无动作)
|
||||
*/
|
||||
private String skipType;
|
||||
|
||||
/**
|
||||
* 流程状态
|
||||
*/
|
||||
private String flowStatus;
|
||||
|
||||
/**
|
||||
* 任务状态
|
||||
*/
|
||||
private String flowTaskStatus;
|
||||
|
||||
/**
|
||||
* 流程状态
|
||||
*/
|
||||
private String flowStatusName;
|
||||
|
||||
/**
|
||||
* 审批意见
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 业务详情 存业务类的json
|
||||
*/
|
||||
private String ext;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 申请人
|
||||
*/
|
||||
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "createBy")
|
||||
private String createByName;
|
||||
|
||||
/**
|
||||
* 流程分类id
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 流程分类名称
|
||||
*/
|
||||
@Translation(type = FlowConstant.CATEGORY_ID_TO_NAME, mapper = "category")
|
||||
private String categoryName;
|
||||
|
||||
/**
|
||||
* 审批表单是否自定义(Y是 N否)
|
||||
*/
|
||||
private String formCustom;
|
||||
|
||||
/**
|
||||
* 审批表单路径
|
||||
*/
|
||||
private String formPath;
|
||||
|
||||
/**
|
||||
* 流程定义编码
|
||||
*/
|
||||
private String flowCode;
|
||||
|
||||
/**
|
||||
* 流程版本号
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 运行时长
|
||||
*/
|
||||
private String runDuration;
|
||||
|
||||
//业务扩展信息开始
|
||||
/**
|
||||
* 业务编码
|
||||
*/
|
||||
private String businessCode;
|
||||
|
||||
/**
|
||||
* 业务标题
|
||||
*/
|
||||
private String businessTitle;
|
||||
//业务扩展信息结束
|
||||
|
||||
/**
|
||||
* 设置创建时间并计算任务运行时长
|
||||
*
|
||||
* @param createTime 创建时间
|
||||
*/
|
||||
public void setCreateTime(LocalDateTime createTime) {
|
||||
this.createTime = createTime;
|
||||
updateRunDuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置更新时间并计算任务运行时长
|
||||
*
|
||||
* @param updateTime 更新时间
|
||||
*/
|
||||
public void setUpdateTime(LocalDateTime updateTime) {
|
||||
this.updateTime = updateTime;
|
||||
updateRunDuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新运行时长
|
||||
*/
|
||||
private void updateRunDuration() {
|
||||
// 如果创建时间和更新时间均不为空,计算它们之间的时长
|
||||
if (this.updateTime != null && this.createTime != null) {
|
||||
this.runDuration = DateUtils.formatBetweenBySecond(this.updateTime, this.createTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置协作方式,并通过协作方式获取名称
|
||||
*/
|
||||
public void setCooperateType(Integer cooperateType) {
|
||||
this.cooperateType = cooperateType;
|
||||
this.cooperateTypeName = CooperateType.getValueByKey(cooperateType);
|
||||
}
|
||||
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
package org.dromara.workflow.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.common.translation.annotation.Translation;
|
||||
import org.dromara.common.translation.constant.TransConstant;
|
||||
import org.dromara.workflow.common.constant.FlowConstant;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 流程实例视图对象。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class FlowInstanceVo {
|
||||
|
||||
/**
|
||||
* 流程实例ID。
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 对应flow_definition表的id
|
||||
*/
|
||||
private Long definitionId;
|
||||
|
||||
/**
|
||||
* 流程定义名称
|
||||
*/
|
||||
private String flowName;
|
||||
|
||||
/**
|
||||
* 流程定义编码
|
||||
*/
|
||||
private String flowCode;
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
private String businessId;
|
||||
|
||||
/**
|
||||
* 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
|
||||
*/
|
||||
private Integer nodeType;
|
||||
|
||||
/**
|
||||
* 流程节点编码 每个流程的nodeCode是唯一的,即definitionId+nodeCode唯一,在数据库层面做了控制
|
||||
*/
|
||||
private String nodeCode;
|
||||
|
||||
/**
|
||||
* 流程节点名称
|
||||
*/
|
||||
private String nodeName;
|
||||
|
||||
/**
|
||||
* 流程变量
|
||||
*/
|
||||
private String variable;
|
||||
|
||||
/**
|
||||
* 流程状态
|
||||
*/
|
||||
private String flowStatus;
|
||||
|
||||
/**
|
||||
* 流程状态
|
||||
*/
|
||||
private String flowStatusName;
|
||||
|
||||
/**
|
||||
* 流程激活状态(0挂起 1激活)
|
||||
*/
|
||||
private Integer activityStatus;
|
||||
|
||||
/**
|
||||
* 审批表单是否自定义(Y是 N否)
|
||||
*/
|
||||
private String formCustom;
|
||||
|
||||
/**
|
||||
* 审批表单路径
|
||||
*/
|
||||
private String formPath;
|
||||
|
||||
/**
|
||||
* 扩展字段,预留给业务系统使用
|
||||
*/
|
||||
private String ext;
|
||||
|
||||
/**
|
||||
* 流程定义版本
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 申请人
|
||||
*/
|
||||
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "createBy")
|
||||
private String createByName;
|
||||
|
||||
/**
|
||||
* 流程分类id
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 流程分类名称
|
||||
*/
|
||||
@Translation(type = FlowConstant.CATEGORY_ID_TO_NAME, mapper = "category")
|
||||
private String categoryName;
|
||||
|
||||
//业务扩展信息开始
|
||||
/**
|
||||
* 业务编码
|
||||
*/
|
||||
private String businessCode;
|
||||
|
||||
/**
|
||||
* 业务标题
|
||||
*/
|
||||
private String businessTitle;
|
||||
//业务扩展信息结束
|
||||
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package org.dromara.workflow.domain.vo;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import org.dromara.common.excel.annotation.ExcelDictFormat;
|
||||
import org.dromara.common.excel.convert.ExcelDictConvert;
|
||||
import org.dromara.workflow.domain.FlowSpel;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
|
||||
/**
|
||||
* 流程spel表达式定义视图对象 flow_spel
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
* @date 2025-07-04
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = FlowSpel.class)
|
||||
public class FlowSpelVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
@ExcelProperty(value = "主键id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 组件名称
|
||||
*/
|
||||
@ExcelProperty(value = "组件名称")
|
||||
private String componentName;
|
||||
|
||||
/**
|
||||
* 方法名
|
||||
*/
|
||||
@ExcelProperty(value = "方法名")
|
||||
private String methodName;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
@ExcelProperty(value = "参数")
|
||||
private String methodParams;
|
||||
|
||||
/**
|
||||
* 预览spel值
|
||||
*/
|
||||
@ExcelProperty(value = "预览spel值")
|
||||
private String viewSpel;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
@ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "0=正常,1=停用")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@ExcelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@ExcelProperty(value = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
package org.dromara.workflow.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.common.translation.annotation.Translation;
|
||||
import org.dromara.common.translation.constant.TransConstant;
|
||||
import org.dromara.warm.flow.core.entity.User;
|
||||
import org.dromara.workflow.common.constant.FlowConstant;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 流程任务视图对象。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Data
|
||||
public class FlowTaskVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 任务ID。
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 对应flow_definition表的id
|
||||
*/
|
||||
private Long definitionId;
|
||||
|
||||
/**
|
||||
* 流程实例表id
|
||||
*/
|
||||
private Long instanceId;
|
||||
|
||||
/**
|
||||
* 流程定义名称
|
||||
*/
|
||||
private String flowName;
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
private String businessId;
|
||||
|
||||
/**
|
||||
* 节点编码
|
||||
*/
|
||||
private String nodeCode;
|
||||
|
||||
/**
|
||||
* 节点名称
|
||||
*/
|
||||
private String nodeName;
|
||||
|
||||
/**
|
||||
* 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
|
||||
*/
|
||||
private Integer nodeType;
|
||||
|
||||
/**
|
||||
* 权限标识 permissionFlag的list形式
|
||||
*/
|
||||
private List<String> permissionList;
|
||||
|
||||
/**
|
||||
* 流程用户列表
|
||||
*/
|
||||
private List<User> userList;
|
||||
|
||||
/**
|
||||
* 审批表单是否自定义(Y是 N否)
|
||||
*/
|
||||
private String formCustom;
|
||||
|
||||
/**
|
||||
* 审批表单
|
||||
*/
|
||||
private String formPath;
|
||||
|
||||
/**
|
||||
* 流程定义编码
|
||||
*/
|
||||
private String flowCode;
|
||||
|
||||
/**
|
||||
* 流程版本号
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 流程状态
|
||||
*/
|
||||
private String flowStatus;
|
||||
|
||||
/**
|
||||
* 流程分类id
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 流程分类名称
|
||||
*/
|
||||
@Translation(type = FlowConstant.CATEGORY_ID_TO_NAME, mapper = "category")
|
||||
private String categoryName;
|
||||
|
||||
/**
|
||||
* 流程状态
|
||||
*/
|
||||
@Translation(type = TransConstant.DICT_TYPE_TO_LABEL, mapper = "flowStatus", other = "wf_business_status")
|
||||
private String flowStatusName;
|
||||
|
||||
/**
|
||||
* 办理人类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 办理人ids
|
||||
*/
|
||||
private String assigneeIds;
|
||||
|
||||
/**
|
||||
* 办理人名称
|
||||
*/
|
||||
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "assigneeIds")
|
||||
private String assigneeNames;
|
||||
|
||||
/**
|
||||
* 抄送人id
|
||||
*/
|
||||
private String processedBy;
|
||||
|
||||
/**
|
||||
* 抄送人名称
|
||||
*/
|
||||
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "processedBy")
|
||||
private String processedByName;
|
||||
|
||||
/**
|
||||
* 流程签署比例值 大于0为票签,会签
|
||||
*/
|
||||
private String nodeRatio;
|
||||
|
||||
/**
|
||||
* 申请人id
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 申请人名称
|
||||
*/
|
||||
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "createBy")
|
||||
private String createByName;
|
||||
|
||||
/**
|
||||
* 是否为申请人节点
|
||||
*/
|
||||
private Boolean applyNode;
|
||||
|
||||
/**
|
||||
* 按钮权限
|
||||
*/
|
||||
private List<ButtonPermissionVo> buttonList;
|
||||
|
||||
/**
|
||||
* 抄送对象 ID 集合
|
||||
* <p>
|
||||
* 根据扩展属性中 CopySettingEnum 类型的数据生成,存储需要抄送的对象 ID
|
||||
*/
|
||||
private List<FlowCopyVo> copyList;
|
||||
|
||||
/**
|
||||
* 自定义参数 Map
|
||||
* <p>
|
||||
* 根据扩展属性中 VariablesEnum 类型的数据生成,存储 key=value 格式的自定义参数
|
||||
*/
|
||||
private Map<String, String> varList;
|
||||
|
||||
//业务扩展信息开始
|
||||
/**
|
||||
* 业务编码
|
||||
*/
|
||||
private String businessCode;
|
||||
|
||||
/**
|
||||
* 业务标题
|
||||
*/
|
||||
private String businessTitle;
|
||||
//业务扩展信息结束
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package org.dromara.workflow.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Node 扩展属性解析结果 VO
|
||||
* <p>
|
||||
* 用于封装从扩展属性 JSON 中解析出的各类信息,包括按钮权限、抄送对象和自定义参数。
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@Data
|
||||
public class NodeExtVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 按钮权限列表
|
||||
* <p>
|
||||
* 根据扩展属性中 ButtonPermissionEnum 类型的数据生成,每个元素表示一个按钮及其是否勾选。
|
||||
*/
|
||||
private List<ButtonPermissionVo> buttonPermissions;
|
||||
|
||||
/**
|
||||
* 抄送对象 ID 集合
|
||||
* <p>
|
||||
* 根据扩展属性中 CopySettingEnum 类型的数据生成,存储需要抄送的对象 ID
|
||||
*/
|
||||
private Set<String> copySettings;
|
||||
|
||||
/**
|
||||
* 自定义参数 Map
|
||||
* <p>
|
||||
* 根据扩展属性中 VariablesEnum 类型的数据生成,存储 key=value 格式的自定义参数
|
||||
*/
|
||||
private Map<String, String> variables;
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package org.dromara.workflow.domain.vo;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import org.dromara.workflow.domain.TestLeave;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
|
||||
/**
|
||||
* 请假视图对象 test_leave
|
||||
*
|
||||
* @author may
|
||||
* @date 2023-07-21
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = TestLeave.class)
|
||||
public class TestLeaveVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@ExcelProperty(value = "主键")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 申请编号
|
||||
*/
|
||||
@ExcelProperty(value = "申请编号")
|
||||
private String applyCode;
|
||||
|
||||
/**
|
||||
* 请假类型
|
||||
*/
|
||||
@ExcelProperty(value = "请假类型")
|
||||
private String leaveType;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
@ExcelProperty(value = "开始时间")
|
||||
private LocalDateTime startDate;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
@ExcelProperty(value = "结束时间")
|
||||
private LocalDateTime endDate;
|
||||
|
||||
/**
|
||||
* 请假天数
|
||||
*/
|
||||
@ExcelProperty(value = "请假天数")
|
||||
private Integer leaveDays;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@ExcelProperty(value = "请假原因")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@ExcelProperty(value = "状态")
|
||||
private String status;
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package org.dromara.workflow.event;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import org.dromara.warm.flow.core.entity.Task;
|
||||
import org.dromara.workflow.domain.bo.FlowCopyBo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工作流抄送事件。
|
||||
*
|
||||
* @param task 当前任务
|
||||
* @param flowCopyList 抄送人列表
|
||||
*/
|
||||
public record WorkflowCopyEvent(Task task, List<FlowCopyBo> flowCopyList) {
|
||||
|
||||
public WorkflowCopyEvent {
|
||||
if (CollUtil.isNotEmpty(flowCopyList)) {
|
||||
flowCopyList = List.copyOf(flowCopyList);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package org.dromara.workflow.event;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工作流结果消息事件。
|
||||
*
|
||||
* @param flowName 流程名称
|
||||
* @param status 流程状态
|
||||
* @param createBy 发起人用户 ID
|
||||
* @param messageType 消息类型
|
||||
*/
|
||||
public record WorkflowResultMessageEvent(String flowName, String status, String createBy, List<String> messageType) {
|
||||
|
||||
public WorkflowResultMessageEvent {
|
||||
if (CollUtil.isNotEmpty(messageType)) {
|
||||
messageType = List.copyOf(messageType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package org.dromara.workflow.event;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工作流待办消息事件。
|
||||
*
|
||||
* @param flowName 流程名称
|
||||
* @param instanceId 流程实例 ID
|
||||
* @param messageType 消息类型
|
||||
* @param notice 通知内容
|
||||
*/
|
||||
public record WorkflowTaskMessageEvent(String flowName, Long instanceId, List<String> messageType, String notice) {
|
||||
|
||||
public WorkflowTaskMessageEvent {
|
||||
if (CollUtil.isNotEmpty(messageType)) {
|
||||
messageType = List.copyOf(messageType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package org.dromara.workflow.handler;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.warm.flow.core.exception.FlowException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* 工作流失败异常
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class FlowExceptionHandler {
|
||||
|
||||
/**
|
||||
* 工作流失败异常。
|
||||
*
|
||||
* @param e 异常信息
|
||||
* @param request 当前请求
|
||||
* @return 统一失败响应
|
||||
*/
|
||||
@ExceptionHandler(FlowException.class)
|
||||
public R<Void> handleFlowException(FlowException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}'", requestURI, e);
|
||||
return R.fail(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
package org.dromara.workflow.handler;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.warm.flow.core.entity.Instance;
|
||||
import org.dromara.warm.flow.core.entity.Task;
|
||||
import org.dromara.workflow.api.event.ProcessDeleteEvent;
|
||||
import org.dromara.workflow.api.event.ProcessEvent;
|
||||
import org.dromara.workflow.api.event.ProcessTaskEvent;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 流程监听服务
|
||||
*
|
||||
* @author may
|
||||
* @date 2024-06-02
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FlowProcessEventHandler {
|
||||
|
||||
/**
|
||||
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成等)
|
||||
*
|
||||
* @param flowCode 流程定义编码
|
||||
* @param instance 实例数据
|
||||
* @param status 流程状态
|
||||
* @param params 办理参数
|
||||
* @param submit 当为true时为申请人节点办理
|
||||
*/
|
||||
public void processHandler(String flowCode, Instance instance, String status, Map<String, Object> params, boolean submit) {
|
||||
log.info("【流程事件发布】流程编码: {}, 业务ID: {}, 流程状态: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 是否申请人节点: {}, 参数: {}",
|
||||
flowCode, instance.getBusinessId(), status, instance.getNodeType(), instance.getNodeCode(), instance.getNodeName(), submit, params);
|
||||
ProcessEvent processEvent = new ProcessEvent();
|
||||
processEvent.setFlowCode(flowCode);
|
||||
processEvent.setInstanceId(instance.getId());
|
||||
processEvent.setBusinessId(instance.getBusinessId());
|
||||
processEvent.setNodeType(instance.getNodeType());
|
||||
processEvent.setNodeCode(instance.getNodeCode());
|
||||
processEvent.setNodeName(instance.getNodeName());
|
||||
processEvent.setStatus(status);
|
||||
processEvent.setParams(params);
|
||||
processEvent.setSubmit(submit);
|
||||
SpringUtils.context().publishEvent(processEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行创建任务监听
|
||||
*
|
||||
* @param flowCode 流程定义编码
|
||||
* @param instance 实例数据
|
||||
* @param nextTask 任务
|
||||
* @param params 上一个任务的办理参数
|
||||
*/
|
||||
public void processTaskHandler(String flowCode, Instance instance, Task nextTask, Map<String, Object> params) {
|
||||
log.info("【流程任务事件发布】流程编码: {}, 业务ID: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 任务ID: {}",
|
||||
flowCode, instance.getBusinessId(), nextTask.getNodeType(), nextTask.getNodeCode(), nextTask.getNodeName(), nextTask.getId());
|
||||
ProcessTaskEvent processTaskEvent = new ProcessTaskEvent();
|
||||
processTaskEvent.setFlowCode(flowCode);
|
||||
processTaskEvent.setInstanceId(instance.getId());
|
||||
processTaskEvent.setBusinessId(instance.getBusinessId());
|
||||
processTaskEvent.setNodeType(nextTask.getNodeType());
|
||||
processTaskEvent.setNodeCode(nextTask.getNodeCode());
|
||||
processTaskEvent.setNodeName(nextTask.getNodeName());
|
||||
processTaskEvent.setTaskId(nextTask.getId());
|
||||
processTaskEvent.setStatus(instance.getFlowStatus());
|
||||
processTaskEvent.setParams(params);
|
||||
SpringUtils.context().publishEvent(processTaskEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除流程监听
|
||||
*
|
||||
* @param flowCode 流程定义编码
|
||||
* @param businessId 业务ID
|
||||
*/
|
||||
public void processDeleteHandler(String flowCode, String businessId) {
|
||||
log.info("【流程删除事件发布】流程编码: {}, 业务ID: {}", flowCode, businessId);
|
||||
ProcessDeleteEvent processDeleteEvent = new ProcessDeleteEvent();
|
||||
processDeleteEvent.setFlowCode(flowCode);
|
||||
processDeleteEvent.setBusinessId(businessId);
|
||||
SpringUtils.context().publishEvent(processDeleteEvent);
|
||||
}
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package org.dromara.workflow.handler;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.system.api.domain.UserDTO;
|
||||
import org.dromara.warm.flow.core.dto.FlowParams;
|
||||
import org.dromara.warm.flow.core.handler.PermissionHandler;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.service.IFlwTaskAssigneeService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 办理人权限处理器
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
@Slf4j
|
||||
public class WorkflowPermissionHandler implements PermissionHandler {
|
||||
|
||||
private final IFlwTaskAssigneeService flwTaskAssigneeService;
|
||||
|
||||
/**
|
||||
* 办理人权限标识,比如用户,角色,部门等,用于校验是否有权限办理任务
|
||||
* 后续在{@link FlowParams#getPermissionFlag} 中获取
|
||||
* 返回当前用户权限集合
|
||||
*
|
||||
* @return 当前用户权限集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> permissions() {
|
||||
return Collections.singletonList(LoginHelper.getUserIdStr());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前办理人
|
||||
*
|
||||
* @return 当前办理人
|
||||
*/
|
||||
@Override
|
||||
public String getHandler() {
|
||||
return LoginHelper.getUserIdStr();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将预设办理人转换为实际用户 ID 列表。
|
||||
*
|
||||
* @param permissions 预设权限标识列表
|
||||
* @return 用户 ID 列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> convertPermissions(List<String> permissions) {
|
||||
if (CollUtil.isEmpty(permissions)) {
|
||||
return permissions;
|
||||
}
|
||||
String storageIds = CollUtil.join(permissions, StringUtils.SEPARATOR);
|
||||
List<UserDTO> users = flwTaskAssigneeService.fetchUsersByStorageIds(storageIds);
|
||||
return StreamUtils.toList(users, userDTO -> Convert.toStr(userDTO.getUserId()));
|
||||
}
|
||||
}
|
||||
+328
@@ -0,0 +1,328 @@
|
||||
package org.dromara.workflow.listener;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.TypeReference;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.enums.BusinessStatusEnum;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.system.api.UserService;
|
||||
import org.dromara.warm.flow.core.FlowEngine;
|
||||
import org.dromara.warm.flow.core.dto.FlowParams;
|
||||
import org.dromara.warm.flow.core.entity.Definition;
|
||||
import org.dromara.warm.flow.core.entity.Instance;
|
||||
import org.dromara.warm.flow.core.entity.Task;
|
||||
import org.dromara.warm.flow.core.listener.GlobalListener;
|
||||
import org.dromara.warm.flow.core.listener.ListenerVariable;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.common.constant.FlowConstant;
|
||||
import org.dromara.workflow.common.enums.TaskStatusEnum;
|
||||
import org.dromara.workflow.domain.bo.FlowCopyBo;
|
||||
import org.dromara.workflow.domain.vo.NodeExtVo;
|
||||
import org.dromara.workflow.event.WorkflowCopyEvent;
|
||||
import org.dromara.workflow.event.WorkflowResultMessageEvent;
|
||||
import org.dromara.workflow.event.WorkflowTaskMessageEvent;
|
||||
import org.dromara.workflow.handler.FlowProcessEventHandler;
|
||||
import org.dromara.workflow.service.IFlwCommonService;
|
||||
import org.dromara.workflow.service.IFlwInstanceService;
|
||||
import org.dromara.workflow.service.IFlwNodeExtService;
|
||||
import org.dromara.workflow.service.IFlwTaskService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 工作流全局监听器,处理任务流转中的扩展变量、消息和事件发布。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class WorkflowGlobalListener implements GlobalListener {
|
||||
|
||||
private final IFlwTaskService flwTaskService;
|
||||
private final IFlwInstanceService flwInstanceService;
|
||||
private final FlowProcessEventHandler flowProcessEventHandler;
|
||||
private final IFlwCommonService flwCommonService;
|
||||
private final IFlwNodeExtService nodeExtService;
|
||||
private final UserService userService;
|
||||
|
||||
/**
|
||||
* 任务创建回调,当前预留扩展。
|
||||
*
|
||||
* @param listenerVariable 监听器变量
|
||||
*/
|
||||
@Override
|
||||
public void create(ListenerVariable listenerVariable) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务开始办理时解析节点扩展配置。
|
||||
*
|
||||
* @param listenerVariable 监听器变量
|
||||
*/
|
||||
@Override
|
||||
public void start(ListenerVariable listenerVariable) {
|
||||
String ext = listenerVariable.getNode().getExt();
|
||||
if (StringUtils.isNotBlank(ext)) {
|
||||
Map<String, Object> variable = listenerVariable.getVariable();
|
||||
if (CollUtil.isEmpty(variable)) {
|
||||
variable = new HashMap<>();
|
||||
}
|
||||
NodeExtVo nodeExt = nodeExtService.parseNodeExt(ext, variable);
|
||||
Set<String> copyList = nodeExt.getCopySettings();
|
||||
if (CollUtil.isNotEmpty(copyList)) {
|
||||
List<Long> userIds = StreamUtils.toList(copyList, Convert::toLong);
|
||||
Map<Long, String> nickNameMap = userService.selectUserNicksByIds(userIds);
|
||||
List<FlowCopyBo> list = StreamUtils.toList(copyList, x -> {
|
||||
FlowCopyBo bo = new FlowCopyBo();
|
||||
Long id = Convert.toLong(x);
|
||||
bo.setUserId(id);
|
||||
bo.setNickName(nickNameMap.getOrDefault(id, StringUtils.EMPTY));
|
||||
return bo;
|
||||
});
|
||||
variable.put(FlowConstant.FLOW_COPY_LIST, list);
|
||||
}
|
||||
if (CollUtil.isNotEmpty(nodeExt.getVariables())) {
|
||||
variable.putAll(nodeExt.getVariables());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务分派时动态调整办理权限。
|
||||
*
|
||||
* @param listenerVariable 监听器变量
|
||||
*/
|
||||
@Override
|
||||
public void assignment(ListenerVariable listenerVariable) {
|
||||
Map<String, Object> variable = listenerVariable.getVariable();
|
||||
List<Task> nextTasks = listenerVariable.getNextTasks();
|
||||
FlowParams flowParams = listenerVariable.getFlowParams();
|
||||
Definition definition = listenerVariable.getDefinition();
|
||||
Instance instance = listenerVariable.getInstance();
|
||||
String applyNodeCode = flwCommonService.applyNodeCode(definition.getId());
|
||||
String hisStatus = flowParams != null ? flowParams.getHisStatus() : null;
|
||||
|
||||
for (Task flowTask : nextTasks) {
|
||||
String nodeCode = flowTask.getNodeCode();
|
||||
|
||||
// 处理办理或退回时指定办理人的情况
|
||||
if (TaskStatusEnum.PASS.getStatus().equals(hisStatus)) {
|
||||
processTaskPermission(variable, flowTask, hisStatus);
|
||||
} else if (TaskStatusEnum.BACK.getStatus().equals(hisStatus)) {
|
||||
processTaskPermission(variable, flowTask, hisStatus);
|
||||
}
|
||||
|
||||
// 如果是申请节点,则把启动人添加到办理人
|
||||
if (nodeCode.equals(applyNodeCode) && StringUtils.isNotBlank(instance.getCreateBy())) {
|
||||
flowTask.setPermissionList(List.of(instance.getCreateBy()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务权限设置
|
||||
*
|
||||
* @param variable 变量集合
|
||||
* @param flowTask 流程任务
|
||||
* @param taskStatus 任务状态
|
||||
*/
|
||||
private void processTaskPermission(Map<String, Object> variable, Task flowTask, String taskStatus) {
|
||||
String nodeKey = taskStatus + StringUtils.COLON + flowTask.getNodeCode();
|
||||
|
||||
// 检查是否存在状态相关的变量
|
||||
if (!variable.containsKey(nodeKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取用户ID字符串
|
||||
Object userIdsObj = variable.get(nodeKey);
|
||||
if (userIdsObj == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String userIds = userIdsObj.toString();
|
||||
if (StringUtils.isBlank(userIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 分割用户ID并设置权限列表
|
||||
List<String> userIdList = StringUtils.str2List(userIds, StringUtils.SEPARATOR, true, true);
|
||||
if (CollUtil.isNotEmpty(userIdList)) {
|
||||
flowTask.setPermissionList(userIdList);
|
||||
if (TaskStatusEnum.PASS.getStatus().equals(taskStatus)) {
|
||||
// 办理指定人变量只消费一次;驳回指定人变量需要保留给后续重复驳回。
|
||||
variable.remove(nodeKey);
|
||||
FlowEngine.insService().removeVariables(flowTask.getInstanceId(), nodeKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务完成后发布流程事件并处理抄送和通知。
|
||||
*
|
||||
* @param listenerVariable 监听器变量
|
||||
*/
|
||||
@Override
|
||||
public void finish(ListenerVariable listenerVariable) {
|
||||
Instance instance = listenerVariable.getInstance();
|
||||
Definition definition = listenerVariable.getDefinition();
|
||||
Task task = listenerVariable.getTask();
|
||||
List<Task> nextTasks = listenerVariable.getNextTasks();
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
FlowParams flowParams = listenerVariable.getFlowParams();
|
||||
Map<String, Object> variable = new HashMap<>();
|
||||
if (ObjectUtil.isNotNull(flowParams)) {
|
||||
// 历史任务扩展(通常为附件)
|
||||
params.put("hisTaskExt", flowParams.getHisTaskExt());
|
||||
// 办理人
|
||||
params.put("handler", flowParams.getHandler());
|
||||
// 办理意见
|
||||
params.put("message", flowParams.getMessage());
|
||||
variable = flowParams.getVariable();
|
||||
}
|
||||
//申请人提交事件
|
||||
Boolean submit = MapUtil.getBool(variable, FlowConstant.SUBMIT);
|
||||
if (submit != null && submit) {
|
||||
String status = determineFlowStatus(instance);
|
||||
flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, variable, true);
|
||||
} else {
|
||||
// 判断流程状态(发布:撤销,退回,作废,终止,已完成事件)
|
||||
String status = determineFlowStatus(instance);
|
||||
if (StringUtils.isNotBlank(status)) {
|
||||
flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, params, false);
|
||||
notifyInitiatorIfNeeded(definition, instance, status, variable);
|
||||
}
|
||||
if (!BusinessStatusEnum.initialState(instance.getFlowStatus())) {
|
||||
if (task != null && CollUtil.isNotEmpty(nextTasks) && nextTasks.size() == 1
|
||||
&& flwCommonService.applyNodeCode(definition.getId()).equals(nextTasks.getFirst().getNodeCode())) {
|
||||
// 如果为画线指定驳回 线条指定为驳回 驳回得节点为申请人节点 则修改流程状态为退回
|
||||
flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, BusinessStatusEnum.BACK.getStatus(), params, false);
|
||||
notifyInitiatorIfNeeded(definition, instance, BusinessStatusEnum.BACK.getStatus(), variable);
|
||||
// 修改流程实例状态
|
||||
instance.setFlowStatus(BusinessStatusEnum.BACK.getStatus());
|
||||
FlowEngine.insService().updateById(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
//发布任务事件
|
||||
if (CollUtil.isNotEmpty(nextTasks)) {
|
||||
for (Task nextTask : nextTasks) {
|
||||
flowProcessEventHandler.processTaskHandler(definition.getFlowCode(), instance, nextTask, params);
|
||||
}
|
||||
}
|
||||
if (ObjectUtil.isNull(flowParams)) {
|
||||
return;
|
||||
}
|
||||
// 只有办理或者退回的时候才执行消息通知和抄送
|
||||
if (!TaskStatusEnum.isPassOrBack(flowParams.getHisStatus())) {
|
||||
return;
|
||||
}
|
||||
if (ObjectUtil.isNull(variable)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (variable.containsKey(FlowConstant.FLOW_COPY_LIST)) {
|
||||
List<FlowCopyBo> flowCopyList = MapUtil.get(variable, FlowConstant.FLOW_COPY_LIST, new TypeReference<>() {
|
||||
});
|
||||
// 添加抄送人
|
||||
SpringUtils.context().publishEvent(new WorkflowCopyEvent(task, flowCopyList));
|
||||
}
|
||||
if (variable.containsKey(FlowConstant.MESSAGE_TYPE)) {
|
||||
List<String> messageType = MapUtil.get(variable, FlowConstant.MESSAGE_TYPE, new TypeReference<>() {
|
||||
});
|
||||
String notice = MapUtil.getStr(variable, FlowConstant.MESSAGE_NOTICE);
|
||||
// 退回到申请人时只保留“已退回”结果消息,避免再追加一条“新的待办”形成重复提醒。
|
||||
if (shouldSendTaskMessage(flowParams, definition, nextTasks)) {
|
||||
SpringUtils.context().publishEvent(new WorkflowTaskMessageEvent(definition.getFlowName(), instance.getId(), messageType, notice));
|
||||
}
|
||||
}
|
||||
FlowEngine.insService().removeVariables(instance.getId(),
|
||||
FlowConstant.FLOW_COPY_LIST,
|
||||
FlowConstant.MESSAGE_TYPE,
|
||||
FlowConstant.MESSAGE_NOTICE,
|
||||
FlowConstant.SUBMIT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否需要发送后续待办消息。
|
||||
*
|
||||
* @param flowParams 流程参数
|
||||
* @param definition 流程定义
|
||||
* @param nextTasks 后续任务列表
|
||||
* @return 是否发送待办消息
|
||||
*/
|
||||
private boolean shouldSendTaskMessage(FlowParams flowParams, Definition definition, List<Task> nextTasks) {
|
||||
if (flowParams == null || !TaskStatusEnum.BACK.getStatus().equals(flowParams.getHisStatus())) {
|
||||
return true;
|
||||
}
|
||||
if (CollUtil.isEmpty(nextTasks) || nextTasks.size() != 1) {
|
||||
return true;
|
||||
}
|
||||
// 只有“退回到申请人”场景需要拦截待办提醒,其余退回/流转仍然保留待办消息。
|
||||
String applyNodeCode = flwCommonService.applyNodeCode(definition.getId());
|
||||
return !StringUtils.equals(applyNodeCode, nextTasks.getFirst().getNodeCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 在流程完成或退回时通知发起人。
|
||||
*
|
||||
* @param definition 流程定义
|
||||
* @param instance 流程实例
|
||||
* @param status 业务状态
|
||||
* @param variable 流程变量
|
||||
*/
|
||||
private void notifyInitiatorIfNeeded(Definition definition, Instance instance, String status, Map<String, Object> variable) {
|
||||
if (!StringUtils.equalsAny(status, BusinessStatusEnum.FINISH.getStatus(), BusinessStatusEnum.BACK.getStatus())) {
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isBlank(instance.getCreateBy())) {
|
||||
return;
|
||||
}
|
||||
// 已完成、已退回这类结果消息只发给发起人,不再混入处理人待办消息。
|
||||
List<String> messageType = null;
|
||||
if (MapUtil.isNotEmpty(variable) && variable.containsKey(FlowConstant.MESSAGE_TYPE)) {
|
||||
messageType = MapUtil.get(variable, FlowConstant.MESSAGE_TYPE, new TypeReference<>() {
|
||||
});
|
||||
}
|
||||
SpringUtils.context().publishEvent(new WorkflowResultMessageEvent(definition.getFlowName(), status, instance.getCreateBy(), messageType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据流程实例确定最终状态
|
||||
*
|
||||
* @param instance 流程实例
|
||||
* @return 流程最终状态
|
||||
*/
|
||||
private String determineFlowStatus(Instance instance) {
|
||||
String flowStatus = instance.getFlowStatus();
|
||||
if (StringUtils.isNotBlank(flowStatus) && BusinessStatusEnum.initialState(flowStatus)) {
|
||||
log.info("流程实例当前状态: {}", flowStatus);
|
||||
return flowStatus;
|
||||
} else {
|
||||
Long instanceId = instance.getId();
|
||||
if (flwTaskService.isTaskEnd(instanceId)) {
|
||||
String status = BusinessStatusEnum.FINISH.getStatus();
|
||||
// 更新流程状态为已完成
|
||||
flwInstanceService.updateStatus(instanceId, status);
|
||||
log.info("流程已结束,状态更新为: {}", status);
|
||||
return status;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package org.dromara.workflow.listener;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.enums.BusinessStatusEnum;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.system.api.UserService;
|
||||
import org.dromara.system.api.domain.UserDTO;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.common.enums.MessageTypeEnum;
|
||||
import org.dromara.workflow.event.WorkflowCopyEvent;
|
||||
import org.dromara.workflow.event.WorkflowResultMessageEvent;
|
||||
import org.dromara.workflow.event.WorkflowTaskMessageEvent;
|
||||
import org.dromara.workflow.service.IFlwCommonService;
|
||||
import org.dromara.workflow.service.IFlwTaskService;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工作流副作用事件监听器。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class WorkflowSideEffectListener {
|
||||
|
||||
private final IFlwTaskService flwTaskService;
|
||||
private final IFlwCommonService flwCommonService;
|
||||
private final UserService userService;
|
||||
|
||||
/**
|
||||
* 保存工作流抄送记录。
|
||||
*
|
||||
* @param event 工作流抄送事件
|
||||
*/
|
||||
@EventListener
|
||||
public void handleCopy(WorkflowCopyEvent event) {
|
||||
flwTaskService.setCopy(event.task(), event.flowCopyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送工作流待办消息。
|
||||
*
|
||||
* @param event 工作流待办消息事件
|
||||
*/
|
||||
@EventListener
|
||||
public void handleTaskMessage(WorkflowTaskMessageEvent event) {
|
||||
flwCommonService.sendMessage(event.flowName(), event.instanceId(), event.messageType(), event.notice());
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送工作流结果消息。
|
||||
*
|
||||
* @param event 工作流结果消息事件
|
||||
*/
|
||||
@EventListener
|
||||
public void handleResultMessage(WorkflowResultMessageEvent event) {
|
||||
if (!StringUtils.equalsAny(event.status(), BusinessStatusEnum.FINISH.getStatus(), BusinessStatusEnum.BACK.getStatus())) {
|
||||
return;
|
||||
}
|
||||
Long createBy = Convert.toLong(event.createBy(), null);
|
||||
if (createBy == null) {
|
||||
return;
|
||||
}
|
||||
BusinessStatusEnum status = BusinessStatusEnum.getByStatus(event.status());
|
||||
if (status == null) {
|
||||
return;
|
||||
}
|
||||
UserDTO initiator = userService.selectById(createBy);
|
||||
if (initiator == null || initiator.getUserId() == null) {
|
||||
return;
|
||||
}
|
||||
List<String> messageType = CollUtil.isNotEmpty(event.messageType())
|
||||
? event.messageType()
|
||||
: Collections.singletonList(MessageTypeEnum.SYSTEM_MESSAGE.getCode());
|
||||
flwCommonService.sendResultMessage(event.flowName(), status, messageType, Collections.singletonList(initiator));
|
||||
}
|
||||
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package org.dromara.workflow.liteflow.complete;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.query.QueryBuilder;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.warm.flow.core.FlowEngine;
|
||||
import org.dromara.warm.flow.core.dto.FlowParams;
|
||||
import org.dromara.warm.flow.core.entity.User;
|
||||
import org.dromara.warm.flow.core.service.TaskService;
|
||||
import org.dromara.warm.flow.orm.entity.FlowTask;
|
||||
import org.dromara.warm.flow.orm.mapper.FlowTaskMapper;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.context.CompleteTaskContext;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.dromara.workflow.common.constant.FlowConstant.*;
|
||||
|
||||
/**
|
||||
* 执行当前处理人的后续自动审批。
|
||||
* <p>
|
||||
* 仅当流程实例开启自动审批时生效,用于连续跳过同一登录人负责的后续待办任务。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@LiteflowComponent("completeAutoPass")
|
||||
public class CompleteAutoPassComponent extends NodeComponent {
|
||||
|
||||
private final TaskService taskService;
|
||||
private final FlowTaskMapper flowTaskMapper;
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
CompleteTaskContext context = getContextBean(CompleteTaskContext.class);
|
||||
FlowTask flowTask = context.getFlowTask();
|
||||
autoPass(flowTask.getId(), context.getFlowParams(), flowTask.getInstanceId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归跳过当前处理人的后续任务。
|
||||
*
|
||||
* @param taskId 本轮已处理任务 id,用于避免重复跳过同一任务
|
||||
* @param flowParams 当前办理参数,会在自动审批时覆盖为自动审批消息和空抄送变量
|
||||
* @param instanceId 流程实例 id
|
||||
*/
|
||||
private void autoPass(Long taskId, FlowParams flowParams, Long instanceId) {
|
||||
List<FlowTask> flowTaskList = selectByInstId(instanceId);
|
||||
if (CollUtil.isEmpty(flowTaskList)) {
|
||||
return;
|
||||
}
|
||||
List<User> userList = FlowEngine.userService()
|
||||
.getByAssociateds(StreamUtils.toList(flowTaskList, FlowTask::getId));
|
||||
if (CollUtil.isEmpty(userList)) {
|
||||
return;
|
||||
}
|
||||
for (FlowTask task : flowTaskList) {
|
||||
if (task.getId().equals(taskId)) {
|
||||
continue;
|
||||
}
|
||||
// 自动审批只处理当前登录人仍是办理人的后续任务,避免替其他候选人或并行分支误审批。
|
||||
List<User> users = StreamUtils.filter(userList,
|
||||
e -> ObjectUtil.equals(task.getId(), e.getAssociated()) && ObjectUtil.equal(e.getProcessedBy(), LoginHelper.getUserIdStr()));
|
||||
if (CollUtil.isEmpty(users)) {
|
||||
continue;
|
||||
}
|
||||
// 自动审批不继承人工办理时的抄送和消息通知,避免重复发送业务消息。
|
||||
flowParams
|
||||
.message("流程引擎自动审批!")
|
||||
.variable(Map.of(
|
||||
SUBMIT, false,
|
||||
FLOW_COPY_LIST, Collections.emptyList(),
|
||||
MESSAGE_NOTICE, StringUtils.EMPTY));
|
||||
taskService.skip(task.getId(), flowParams);
|
||||
// 跳过后可能生成新的后续任务,继续扫描直到当前登录人不再有可自动办理任务。
|
||||
autoPass(task.getId(), flowParams, instanceId);
|
||||
}
|
||||
}
|
||||
|
||||
private List<FlowTask> selectByInstId(Long instanceId) {
|
||||
return flowTaskMapper.selectList(QueryBuilder.lambda(FlowTask.class)
|
||||
.eq(FlowTask::getInstanceId, instanceId)
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package org.dromara.workflow.liteflow.complete;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.enums.BusinessStatusEnum;
|
||||
import org.dromara.warm.flow.core.dto.FlowParams;
|
||||
import org.dromara.warm.flow.core.enums.SkipType;
|
||||
import org.dromara.warm.flow.core.service.TaskService;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.common.enums.TaskStatusEnum;
|
||||
import org.dromara.workflow.domain.bo.CompleteTaskBo;
|
||||
import org.dromara.workflow.domain.context.CompleteTaskContext;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.dromara.workflow.common.constant.FlowConstant.*;
|
||||
|
||||
/**
|
||||
* 构建办理参数并执行当前任务跳转。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@LiteflowComponent("completeExecute")
|
||||
public class CompleteExecuteComponent extends NodeComponent {
|
||||
|
||||
private final TaskService taskService;
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
CompleteTaskContext context = getContextBean(CompleteTaskContext.class);
|
||||
CompleteTaskBo completeTaskBo = context.getCompleteTaskBo();
|
||||
Map<String, Object> variables = completeTaskBo.getVariables();
|
||||
FlowParams flowParams = FlowParams.build()
|
||||
.handler(completeTaskBo.getHandler())
|
||||
.variable(variables)
|
||||
.ignore(Convert.toBool(variables.getOrDefault(VAR_IGNORE, false)))
|
||||
.ignoreDepute(Convert.toBool(variables.getOrDefault(VAR_IGNORE_DEPUTE, false)))
|
||||
.ignoreCooperate(Convert.toBool(variables.getOrDefault(VAR_IGNORE_COOPERATE, false)))
|
||||
.skipType(SkipType.PASS.getKey())
|
||||
.message(completeTaskBo.getMessage())
|
||||
.flowStatus(BusinessStatusEnum.WAITING.getStatus())
|
||||
.hisStatus(TaskStatusEnum.PASS.getStatus())
|
||||
.hisTaskExt(completeTaskBo.getFileId());
|
||||
|
||||
context.setFlowParams(flowParams);
|
||||
context.setAutoPass(Convert.toBool(context.getInstance().getVariableMap().getOrDefault(AUTO_PASS, false)));
|
||||
taskService.skip(context.getFlowTask().getId(), flowParams);
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package org.dromara.workflow.liteflow.complete;
|
||||
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeBooleanComponent;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.context.CompleteTaskContext;
|
||||
|
||||
/**
|
||||
* 判断办理任务后是否进入自动审批分支。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@LiteflowComponent("completeNeedAutoPass")
|
||||
public class CompleteNeedAutoPassComponent extends NodeBooleanComponent {
|
||||
|
||||
@Override
|
||||
public boolean processBoolean() {
|
||||
CompleteTaskContext context = getContextBean(CompleteTaskContext.class);
|
||||
return Boolean.TRUE.equals(context.getAutoPass());
|
||||
}
|
||||
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package org.dromara.workflow.liteflow.complete;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.enums.BusinessStatusEnum;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.warm.flow.core.entity.Instance;
|
||||
import org.dromara.warm.flow.core.service.InsService;
|
||||
import org.dromara.warm.flow.orm.entity.FlowTask;
|
||||
import org.dromara.warm.flow.orm.mapper.FlowTaskMapper;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.common.enums.TaskStatusEnum;
|
||||
import org.dromara.workflow.domain.bo.CompleteTaskBo;
|
||||
import org.dromara.workflow.domain.context.CompleteTaskContext;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.dromara.workflow.common.constant.FlowConstant.*;
|
||||
|
||||
/**
|
||||
* 准备任务办理上下文。
|
||||
* <p>
|
||||
* 负责加载任务实例、补齐流程变量,并解析弹窗选择的后续办理人。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@LiteflowComponent("completePrepare")
|
||||
public class CompletePrepareComponent extends NodeComponent {
|
||||
|
||||
private final FlowTaskMapper flowTaskMapper;
|
||||
private final InsService insService;
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
CompleteTaskContext context = getContextBean(CompleteTaskContext.class);
|
||||
CompleteTaskBo completeTaskBo = context.getCompleteTaskBo();
|
||||
FlowTask flowTask = flowTaskMapper.selectById(completeTaskBo.getTaskId());
|
||||
if (ObjectUtil.isNull(flowTask)) {
|
||||
throw new ServiceException("流程任务不存在或任务已审批!");
|
||||
}
|
||||
Instance instance = insService.getById(flowTask.getInstanceId());
|
||||
if (ObjectUtil.isNull(instance)) {
|
||||
throw new ServiceException("流程实例不存在");
|
||||
}
|
||||
context.setFlowTask(flowTask);
|
||||
context.setInstance(instance);
|
||||
|
||||
Map<String, Object> variables = completeTaskBo.getVariables();
|
||||
variables.put(FLOW_COPY_LIST, completeTaskBo.getFlowCopyList());
|
||||
variables.put(MESSAGE_TYPE, completeTaskBo.getMessageType());
|
||||
variables.put(MESSAGE_NOTICE, completeTaskBo.getNotice());
|
||||
if (BusinessStatusEnum.isDraftOrCancelOrBack(instance.getFlowStatus())) {
|
||||
// 草稿、撤销、驳回状态再次办理时等同于重新提交,条件表达式通过 SUBMIT 识别提交分支。
|
||||
variables.put(SUBMIT, true);
|
||||
}
|
||||
Map<String, Object> assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), instance.getVariableMap());
|
||||
if (CollUtil.isNotEmpty(assigneeMap)) {
|
||||
variables.putAll(assigneeMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将弹窗处理人映射成通过/退回状态下的办理人变量。
|
||||
*/
|
||||
private Map<String, Object> setPopAssigneeMap(Map<String, Object> assigneeMap, Map<String, Object> variablesMap) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
if (CollUtil.isEmpty(assigneeMap)) {
|
||||
return map;
|
||||
}
|
||||
for (Map.Entry<String, Object> entry : assigneeMap.entrySet()) {
|
||||
if (variablesMap.containsKey(entry.getKey())) {
|
||||
String userIds = variablesMap.get(entry.getKey()).toString();
|
||||
if (StringUtils.isNotBlank(userIds)) {
|
||||
Set<String> hashSet = CollUtil.newHashSet();
|
||||
List<String> popUserIds = StringUtils.str2List(entry.getValue().toString(), StringUtils.SEPARATOR, true, true);
|
||||
List<String> variableUserIds = StringUtils.str2List(userIds, StringUtils.SEPARATOR, true, true);
|
||||
hashSet.addAll(popUserIds);
|
||||
hashSet.addAll(variableUserIds);
|
||||
// Warm-Flow 会按“状态:变量名”读取办理人,正向和退回都写入同一批候选人。
|
||||
map.put(TaskStatusEnum.PASS.getStatus() + StringUtils.COLON + entry.getKey(), StringUtils.joinComma(hashSet));
|
||||
map.put(TaskStatusEnum.BACK.getStatus() + StringUtils.COLON + entry.getKey(), StringUtils.joinComma(hashSet));
|
||||
}
|
||||
} else {
|
||||
map.put(TaskStatusEnum.PASS.getStatus() + StringUtils.COLON + entry.getKey(), entry.getValue());
|
||||
map.put(TaskStatusEnum.BACK.getStatus() + StringUtils.COLON + entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package org.dromara.workflow.liteflow.instance;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.warm.flow.core.entity.Definition;
|
||||
import org.dromara.warm.flow.core.entity.Instance;
|
||||
import org.dromara.warm.flow.core.service.DefService;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.context.InstanceDeleteContext;
|
||||
import org.dromara.workflow.handler.FlowProcessEventHandler;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 校验实例删除权限并发布业务删除事件。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@LiteflowComponent("instanceDeleteEvent")
|
||||
public class InstanceDeleteEventComponent extends NodeComponent {
|
||||
|
||||
private final DefService defService;
|
||||
private final FlowProcessEventHandler flowProcessEventHandler;
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
InstanceDeleteContext context = getContextBean(InstanceDeleteContext.class);
|
||||
String userId = LoginHelper.getUserIdStr();
|
||||
context.getFlowInstances().forEach(flowInstance -> {
|
||||
if (!LoginHelper.isSuperAdmin() && !flowInstance.getCreateBy().equals(userId)) {
|
||||
throw new ServiceException("权限不足,无法删除流程实例信息!");
|
||||
}
|
||||
});
|
||||
|
||||
Map<Long, Definition> definitionMap = StreamUtils.toMap(
|
||||
defService.getByIds(StreamUtils.toList(context.getFlowInstances(), Instance::getDefinitionId)),
|
||||
Definition::getId,
|
||||
Function.identity()
|
||||
);
|
||||
context.getFlowInstances().forEach(instance -> {
|
||||
Definition definition = definitionMap.get(instance.getDefinitionId());
|
||||
if (ObjectUtil.isNull(definition)) {
|
||||
log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId());
|
||||
return;
|
||||
}
|
||||
flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package org.dromara.workflow.liteflow.instance;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.mybatis.core.query.QueryBuilder;
|
||||
import org.dromara.warm.flow.core.FlowEngine;
|
||||
import org.dromara.warm.flow.core.service.InsService;
|
||||
import org.dromara.warm.flow.orm.entity.FlowHisTask;
|
||||
import org.dromara.warm.flow.orm.entity.FlowTask;
|
||||
import org.dromara.warm.flow.orm.mapper.FlowHisTaskMapper;
|
||||
import org.dromara.warm.flow.orm.mapper.FlowTaskMapper;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.common.enums.TaskStatusEnum;
|
||||
import org.dromara.workflow.domain.context.InstanceDeleteContext;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 执行流程实例删除。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@LiteflowComponent("instanceDeleteExecute")
|
||||
public class InstanceDeleteExecuteComponent extends NodeComponent {
|
||||
|
||||
private final InsService insService;
|
||||
private final FlowHisTaskMapper flowHisTaskMapper;
|
||||
private final FlowTaskMapper flowTaskMapper;
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
InstanceDeleteContext context = getContextBean(InstanceDeleteContext.class);
|
||||
deleteCopyUsers(context.getDeleteInstanceIds());
|
||||
if (!context.isHistory()) {
|
||||
context.setResult(insService.remove(context.getDeleteInstanceIds()));
|
||||
return;
|
||||
}
|
||||
List<FlowTask> flowTaskList = flowTaskMapper.selectList(QueryBuilder.lambda(FlowTask.class)
|
||||
.in(FlowTask::getInstanceId, context.getDeleteInstanceIds())
|
||||
.build());
|
||||
if (CollUtil.isNotEmpty(flowTaskList)) {
|
||||
FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTaskList, FlowTask::getId));
|
||||
}
|
||||
FlowEngine.taskService().deleteByInsIds(context.getDeleteInstanceIds());
|
||||
FlowEngine.hisTaskService().deleteByInsIds(context.getDeleteInstanceIds());
|
||||
FlowEngine.insService().removeByIds(context.getDeleteInstanceIds());
|
||||
context.setResult(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除流程实例产生的抄送人员关联。
|
||||
*
|
||||
* @param instanceIds 流程实例 ID 集合
|
||||
*/
|
||||
private void deleteCopyUsers(List<Long> instanceIds) {
|
||||
List<FlowHisTask> copyTasks = flowHisTaskMapper.selectList(QueryBuilder.lambda(FlowHisTask.class)
|
||||
.in(FlowHisTask::getInstanceId, instanceIds)
|
||||
.eq(FlowHisTask::getFlowStatus, TaskStatusEnum.COPY.getStatus())
|
||||
.build());
|
||||
if (CollUtil.isEmpty(copyTasks)) {
|
||||
return;
|
||||
}
|
||||
List<Long> copyTaskIds = copyTasks.stream()
|
||||
.map(FlowHisTask::getTaskId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (CollUtil.isNotEmpty(copyTaskIds)) {
|
||||
FlowEngine.userService().deleteByTaskIds(copyTaskIds);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package org.dromara.workflow.liteflow.instance;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeBooleanComponent;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.context.InstanceDeleteContext;
|
||||
|
||||
/**
|
||||
* 判断删除流程实例时是否存在可执行删除的数据。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@LiteflowComponent("instanceDeleteExists")
|
||||
public class InstanceDeleteExistsComponent extends NodeBooleanComponent {
|
||||
|
||||
@Override
|
||||
public boolean processBoolean() {
|
||||
InstanceDeleteContext context = getContextBean(InstanceDeleteContext.class);
|
||||
return CollUtil.isNotEmpty(context.getFlowInstances());
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package org.dromara.workflow.liteflow.instance;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.mybatis.core.query.QueryBuilder;
|
||||
import org.dromara.warm.flow.orm.entity.FlowInstance;
|
||||
import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.context.InstanceDeleteContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 加载待删除流程实例。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@LiteflowComponent("instanceDeleteLoad")
|
||||
public class InstanceDeleteLoadComponent extends NodeComponent {
|
||||
|
||||
private final FlowInstanceMapper flowInstanceMapper;
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
InstanceDeleteContext context = getContextBean(InstanceDeleteContext.class);
|
||||
List<FlowInstance> flowInstances;
|
||||
if (CollUtil.isNotEmpty(context.getBusinessIds())) {
|
||||
flowInstances = flowInstanceMapper.selectList(QueryBuilder.lambda(FlowInstance.class)
|
||||
.in(FlowInstance::getBusinessId, context.getBusinessIds())
|
||||
.build());
|
||||
context.setDeleteInstanceIds(StreamUtils.toList(flowInstances, FlowInstance::getId));
|
||||
} else {
|
||||
flowInstances = flowInstanceMapper.selectByIds(context.getInstanceIds());
|
||||
context.setDeleteInstanceIds(new ArrayList<>(context.getInstanceIds()));
|
||||
}
|
||||
if (CollUtil.isEmpty(flowInstances)) {
|
||||
log.warn("未找到对应的流程实例信息,无法执行删除操作。");
|
||||
context.setResult(false);
|
||||
return;
|
||||
}
|
||||
context.setFlowInstances(flowInstances);
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package org.dromara.workflow.liteflow.operation;
|
||||
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.warm.flow.core.service.TaskService;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.common.enums.TaskStatusEnum;
|
||||
import org.dromara.workflow.domain.context.TaskOperationContext;
|
||||
|
||||
/**
|
||||
* 执行委派、转办、加签或减签。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@LiteflowComponent("taskOpExecute")
|
||||
public class TaskOpExecuteComponent extends NodeComponent {
|
||||
|
||||
private final TaskService taskService;
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
TaskOperationContext context = getContextBean(TaskOperationContext.class);
|
||||
Long taskId = context.getTaskOperationBo().getTaskId();
|
||||
boolean result = false;
|
||||
switch (context.getOperation()) {
|
||||
case DELEGATE_TASK -> {
|
||||
context.getFlowParams().hisStatus(TaskStatusEnum.DEPUTE.getStatus());
|
||||
result = taskService.depute(taskId, context.getFlowParams());
|
||||
}
|
||||
case TRANSFER_TASK -> {
|
||||
context.getFlowParams().hisStatus(TaskStatusEnum.TRANSFER.getStatus());
|
||||
result = taskService.transfer(taskId, context.getFlowParams());
|
||||
}
|
||||
case ADD_SIGNATURE -> {
|
||||
context.getFlowParams().hisStatus(TaskStatusEnum.SIGN.getStatus());
|
||||
result = taskService.addSignature(taskId, context.getFlowParams());
|
||||
}
|
||||
case REDUCTION_SIGNATURE -> {
|
||||
context.getFlowParams().hisStatus(TaskStatusEnum.SIGN_OFF.getStatus());
|
||||
result = taskService.reductionSignature(taskId, context.getFlowParams());
|
||||
}
|
||||
}
|
||||
context.setResult(result);
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package org.dromara.workflow.liteflow.operation;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.mybatis.core.query.QueryBuilder;
|
||||
import org.dromara.warm.flow.core.entity.Task;
|
||||
import org.dromara.warm.flow.core.enums.CooperateType;
|
||||
import org.dromara.warm.flow.core.service.TaskService;
|
||||
import org.dromara.warm.flow.orm.entity.FlowNode;
|
||||
import org.dromara.warm.flow.orm.mapper.FlowNodeMapper;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.common.enums.TaskOperationEnum;
|
||||
import org.dromara.workflow.domain.context.TaskOperationContext;
|
||||
|
||||
/**
|
||||
* 加载任务节点并校验操作约束。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@LiteflowComponent("taskOpLoad")
|
||||
public class TaskOpLoadComponent extends NodeComponent {
|
||||
|
||||
private final TaskService taskService;
|
||||
private final FlowNodeMapper flowNodeMapper;
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
TaskOperationContext context = getContextBean(TaskOperationContext.class);
|
||||
Task task = taskService.getById(context.getTaskOperationBo().getTaskId());
|
||||
if (ObjectUtil.isNull(task)) {
|
||||
throw new ServiceException("任务不存在!");
|
||||
}
|
||||
FlowNode flowNode = flowNodeMapper.selectOne(QueryBuilder.lambda(FlowNode.class)
|
||||
.eq(FlowNode::getNodeCode, task.getNodeCode())
|
||||
.eq(FlowNode::getDefinitionId, task.getDefinitionId())
|
||||
.build());
|
||||
if (ObjectUtil.isNull(flowNode)) {
|
||||
throw new ServiceException("流程节点不存在");
|
||||
}
|
||||
if ((context.getOperation() == TaskOperationEnum.ADD_SIGNATURE || context.getOperation() == TaskOperationEnum.REDUCTION_SIGNATURE)
|
||||
&& CooperateType.isOrSign(flowNode.getNodeRatio())) {
|
||||
throw new ServiceException(task.getNodeName() + "不是会签或票签节点!");
|
||||
}
|
||||
context.setTask(task);
|
||||
context.setFlowNode(flowNode);
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package org.dromara.workflow.liteflow.operation;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeBooleanComponent;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.context.TaskOperationContext;
|
||||
|
||||
/**
|
||||
* 判断任务操作后是否进入消息通知分支。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@LiteflowComponent("taskOpNeedNotify")
|
||||
public class TaskOpNeedNotifyComponent extends NodeBooleanComponent {
|
||||
|
||||
@Override
|
||||
public boolean processBoolean() {
|
||||
TaskOperationContext context = getContextBean(TaskOperationContext.class);
|
||||
return context.isResult() && CollUtil.isNotEmpty(context.getTaskOperationBo().getMessageType());
|
||||
}
|
||||
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package org.dromara.workflow.liteflow.operation;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.system.api.UserService;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.bo.TaskOperationBo;
|
||||
import org.dromara.workflow.domain.context.TaskOperationContext;
|
||||
import org.dromara.workflow.service.IFlwCommonService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.dromara.workflow.common.constant.FlowConstant.PATH_TASK_WAITING;
|
||||
|
||||
/**
|
||||
* 任务操作成功后发送待办消息。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@LiteflowComponent("taskOpNotify")
|
||||
public class TaskOpNotifyComponent extends NodeComponent {
|
||||
|
||||
private final IFlwCommonService flwCommonService;
|
||||
private final UserService userService;
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
TaskOperationContext context = getContextBean(TaskOperationContext.class);
|
||||
TaskOperationBo bo = context.getTaskOperationBo();
|
||||
List<Long> userIdList = new ArrayList<>();
|
||||
if (StringUtils.isNotBlank(bo.getUserId())) {
|
||||
userIdList.add(Convert.toLong(bo.getUserId()));
|
||||
}
|
||||
if (CollUtil.isNotEmpty(bo.getUserIds())) {
|
||||
userIdList.addAll(bo.getUserIds().stream().map(Convert::toLong).toList());
|
||||
}
|
||||
if (CollUtil.isEmpty(userIdList)) {
|
||||
return;
|
||||
}
|
||||
// 转办、委托、加减签等运行时操作,消息接收人统一从“我的待办”进入处理。
|
||||
flwCommonService.sendMessage(
|
||||
bo.getMessageType(),
|
||||
StringUtils.isNotBlank(bo.getMessage()) ? bo.getMessage() : "单据「" + context.getOperation().getDesc() + "」通知",
|
||||
"单据「" + context.getOperation().getDesc() + "」提醒",
|
||||
userService.selectListByIds(userIdList),
|
||||
PATH_TASK_WAITING
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package org.dromara.workflow.liteflow.operation;
|
||||
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.ValidatorUtils;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.warm.flow.core.dto.FlowParams;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.common.enums.TaskOperationEnum;
|
||||
import org.dromara.workflow.domain.bo.TaskOperationBo;
|
||||
import org.dromara.workflow.domain.context.TaskOperationContext;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* 准备任务操作参数。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Slf4j
|
||||
@LiteflowComponent("taskOpPrepare")
|
||||
public class TaskOpPrepareComponent extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
TaskOperationContext context = getContextBean(TaskOperationContext.class);
|
||||
TaskOperationEnum op = TaskOperationEnum.getByCode(context.getTaskOperation());
|
||||
if (op == null) {
|
||||
log.error("Invalid operation type:{} ", context.getTaskOperation());
|
||||
throw new ServiceException("Invalid operation type " + context.getTaskOperation());
|
||||
}
|
||||
context.setOperation(op);
|
||||
|
||||
TaskOperationBo bo = context.getTaskOperationBo();
|
||||
switch (op) {
|
||||
case DELEGATE_TASK, TRANSFER_TASK -> ValidatorUtils.validate(bo, AddGroup.class);
|
||||
case ADD_SIGNATURE, REDUCTION_SIGNATURE -> ValidatorUtils.validate(bo, EditGroup.class);
|
||||
}
|
||||
|
||||
FlowParams flowParams = FlowParams.build().message(bo.getMessage());
|
||||
if (LoginHelper.isSuperAdmin()) {
|
||||
flowParams.ignore(true);
|
||||
}
|
||||
switch (op) {
|
||||
case DELEGATE_TASK, TRANSFER_TASK -> flowParams.addHandlers(Collections.singletonList(bo.getUserId()));
|
||||
case ADD_SIGNATURE -> flowParams.addHandlers(bo.getUserIds());
|
||||
case REDUCTION_SIGNATURE -> flowParams.reductionHandlers(bo.getUserIds());
|
||||
}
|
||||
context.setFlowParams(flowParams);
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package org.dromara.workflow.liteflow.start;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.enums.BusinessStatusEnum;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.warm.flow.core.dto.FlowParams;
|
||||
import org.dromara.warm.flow.core.entity.Instance;
|
||||
import org.dromara.warm.flow.core.entity.Task;
|
||||
import org.dromara.warm.flow.core.service.InsService;
|
||||
import org.dromara.warm.flow.core.service.TaskService;
|
||||
import org.dromara.warm.flow.orm.entity.FlowTask;
|
||||
import org.dromara.workflow.api.domain.StartProcessReturnDTO;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.FlowInstanceBizExt;
|
||||
import org.dromara.workflow.domain.context.StartProcessContext;
|
||||
import org.dromara.workflow.mapper.FlwInstanceBizExtMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 启动流程实例并构建返回结果。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@LiteflowComponent("startExecute")
|
||||
public class StartExecuteComponent extends NodeComponent {
|
||||
|
||||
private final InsService insService;
|
||||
private final TaskService taskService;
|
||||
private final FlwInstanceBizExtMapper flwInstanceBizExtMapper;
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
StartProcessContext context = getContextBean(StartProcessContext.class);
|
||||
FlowParams flowParams = FlowParams.build()
|
||||
.handler(context.getStartProcessBo().getHandler())
|
||||
.flowCode(context.getStartProcessBo().getFlowCode())
|
||||
.variable(context.getVariables())
|
||||
.flowStatus(BusinessStatusEnum.DRAFT.getStatus());
|
||||
Instance instance = insService.start(context.getBusinessId(), flowParams);
|
||||
context.setInstance(instance);
|
||||
saveBizExt(context, instance);
|
||||
|
||||
List<Task> taskList = taskService.list(new FlowTask().setInstanceId(instance.getId()));
|
||||
if (CollUtil.isEmpty(taskList)) {
|
||||
throw new ServiceException("流程启动失败,未生成任务");
|
||||
}
|
||||
if (taskList.size() > 1) {
|
||||
throw new ServiceException("请检查流程第一个环节是否为申请人!");
|
||||
}
|
||||
context.setTaskList(taskList);
|
||||
context.setStartProcessReturn(new StartProcessReturnDTO(instance.getId(), taskList.getFirst().getId()));
|
||||
}
|
||||
|
||||
private void saveBizExt(StartProcessContext context, Instance instance) {
|
||||
FlowInstanceBizExt bizExt = context.getBizExt();
|
||||
bizExt.setInstanceId(instance.getId());
|
||||
bizExt.setBusinessId(instance.getBusinessId());
|
||||
flwInstanceBizExtMapper.saveOrUpdateByInstanceId(bizExt);
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package org.dromara.workflow.liteflow.start;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeBooleanComponent;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.context.StartProcessContext;
|
||||
|
||||
/**
|
||||
* 判断启动流程时是否走已有实例续提交分支。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@LiteflowComponent("startExists")
|
||||
public class StartExistsComponent extends NodeBooleanComponent {
|
||||
|
||||
@Override
|
||||
public boolean processBoolean() {
|
||||
StartProcessContext context = getContextBean(StartProcessContext.class);
|
||||
return ObjectUtil.isNotNull(context.getExistingInstance());
|
||||
}
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package org.dromara.workflow.liteflow.start;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.warm.flow.core.FlowEngine;
|
||||
import org.dromara.warm.flow.core.entity.Definition;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.FlowInstanceBizExt;
|
||||
import org.dromara.workflow.domain.context.StartProcessContext;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.dromara.workflow.common.constant.FlowConstant.*;
|
||||
|
||||
/**
|
||||
* 加载流程定义并补齐启动变量。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@LiteflowComponent("startPrepareInstance")
|
||||
public class StartPrepareInstanceComponent extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
StartProcessContext context = getContextBean(StartProcessContext.class);
|
||||
Definition definition = FlowEngine.defService().getPublishByFlowCode(context.getStartProcessBo().getFlowCode());
|
||||
if (ObjectUtil.isNull(definition)) {
|
||||
throw new ServiceException("流程【" + context.getStartProcessBo().getFlowCode() + "】未发布,请先在流程设计器中发布流程定义");
|
||||
}
|
||||
context.setDefinition(definition);
|
||||
|
||||
Map<String, Object> variables = context.getVariables();
|
||||
Dict dict = JsonUtils.parseMap(definition.getExt());
|
||||
boolean autoPass = !ObjectUtil.isNull(dict) && dict.getBool(AUTO_PASS);
|
||||
variables.put(AUTO_PASS, autoPass);
|
||||
variables.put(BUSINESS_CODE, generateBusinessCode(context.getBizExt()));
|
||||
}
|
||||
|
||||
private String generateBusinessCode(FlowInstanceBizExt bizExt) {
|
||||
if (ObjectUtil.isNull(bizExt.getBusinessCode()) || bizExt.getBusinessCode().isBlank()) {
|
||||
// TODO: 按照自己业务规则生成编号
|
||||
String businessCode = Convert.toStr(System.currentTimeMillis());
|
||||
bizExt.setBusinessCode(businessCode);
|
||||
return businessCode;
|
||||
}
|
||||
return bizExt.getBusinessCode();
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package org.dromara.workflow.liteflow.start;
|
||||
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.query.QueryBuilder;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.warm.flow.orm.entity.FlowInstance;
|
||||
import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.context.StartProcessContext;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.dromara.workflow.common.constant.FlowConstant.*;
|
||||
|
||||
/**
|
||||
* 准备流程启动请求。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@LiteflowComponent("startPrepareRequest")
|
||||
public class StartPrepareRequestComponent extends NodeComponent {
|
||||
|
||||
private final FlowInstanceMapper flowInstanceMapper;
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
StartProcessContext context = getContextBean(StartProcessContext.class);
|
||||
String businessId = context.getStartProcessBo().getBusinessId();
|
||||
if (StringUtils.isBlank(businessId)) {
|
||||
throw new ServiceException("启动工作流时必须包含业务ID");
|
||||
}
|
||||
context.setBusinessId(businessId);
|
||||
context.setVariables(context.getStartProcessBo().getVariables());
|
||||
context.setBizExt(context.getStartProcessBo().getBizExt());
|
||||
|
||||
Map<String, Object> variables = context.getVariables();
|
||||
variables.put(INITIATOR, LoginHelper.getUserIdStr());
|
||||
variables.put(INITIATOR_DEPT_ID, LoginHelper.getDeptId());
|
||||
variables.put(BUSINESS_ID, businessId);
|
||||
|
||||
FlowInstance flowInstance = flowInstanceMapper.selectOne(QueryBuilder.lambda(FlowInstance.class)
|
||||
.eq(FlowInstance::getBusinessId, businessId)
|
||||
.build());
|
||||
context.setExistingInstance(flowInstance);
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package org.dromara.workflow.liteflow.start;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.enums.BusinessStatusEnum;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.warm.flow.core.entity.Task;
|
||||
import org.dromara.warm.flow.core.service.InsService;
|
||||
import org.dromara.warm.flow.core.service.TaskService;
|
||||
import org.dromara.warm.flow.orm.entity.FlowTask;
|
||||
import org.dromara.workflow.api.domain.StartProcessReturnDTO;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.FlowInstanceBizExt;
|
||||
import org.dromara.workflow.domain.context.StartProcessContext;
|
||||
import org.dromara.workflow.mapper.FlwInstanceBizExtMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 已存在流程实例时执行续提交。
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@LiteflowComponent("startResume")
|
||||
public class StartResumeComponent extends NodeComponent {
|
||||
|
||||
private final TaskService taskService;
|
||||
private final InsService insService;
|
||||
private final FlwInstanceBizExtMapper flwInstanceBizExtMapper;
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
StartProcessContext context = getContextBean(StartProcessContext.class);
|
||||
BusinessStatusEnum.checkStartStatus(context.getExistingInstance().getFlowStatus());
|
||||
List<Task> taskList = taskService.list(new FlowTask().setInstanceId(context.getExistingInstance().getId()));
|
||||
if (CollUtil.isEmpty(taskList)) {
|
||||
throw new ServiceException("流程实例缺少任务,请检查流程定义配置");
|
||||
}
|
||||
taskService.mergeVariable(context.getExistingInstance(), context.getVariables());
|
||||
insService.updateById(context.getExistingInstance());
|
||||
saveBizExt(context);
|
||||
context.setStartProcessReturn(new StartProcessReturnDTO(taskList.getFirst().getInstanceId(), taskList.getFirst().getId()));
|
||||
}
|
||||
|
||||
private void saveBizExt(StartProcessContext context) {
|
||||
FlowInstanceBizExt bizExt = context.getBizExt();
|
||||
bizExt.setInstanceId(context.getExistingInstance().getId());
|
||||
bizExt.setBusinessId(context.getExistingInstance().getBusinessId());
|
||||
flwInstanceBizExtMapper.saveOrUpdateByInstanceId(bizExt);
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package org.dromara.workflow.mapper;
|
||||
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.workflow.domain.FlowCategory;
|
||||
import org.dromara.workflow.domain.vo.FlowCategoryVo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 流程分类Mapper接口
|
||||
*
|
||||
* @author may
|
||||
* @date 2023-06-27
|
||||
*/
|
||||
public interface FlwCategoryMapper extends BaseMapperPlus<FlowCategory, FlowCategoryVo> {
|
||||
|
||||
/**
|
||||
* 根据父流程分类ID查询其所有子流程分类的列表
|
||||
*
|
||||
* @param parentId 父流程分类ID
|
||||
* @return 包含子流程分类的列表
|
||||
*/
|
||||
default List<FlowCategory> selectListByParentId(Long parentId) {
|
||||
return this.lambda()
|
||||
.select(FlowCategory::getCategoryId)
|
||||
.findInSet(parentId, FlowCategory::getAncestors)
|
||||
.list();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据父流程分类ID查询包括父ID及其所有子流程分类ID的列表
|
||||
*
|
||||
* @param parentId 父流程分类ID
|
||||
* @return 包含父ID和子流程分类ID的列表
|
||||
*/
|
||||
default List<Long> selectCategoryIdsByParentId(Long parentId) {
|
||||
return Stream.concat(
|
||||
this.selectListByParentId(parentId).stream()
|
||||
.map(FlowCategory::getCategoryId),
|
||||
Stream.of(parentId)
|
||||
).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package org.dromara.workflow.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.common.mybatis.core.query.QueryBuilder;
|
||||
import org.dromara.warm.flow.core.enums.NodeType;
|
||||
import org.dromara.warm.flow.orm.entity.FlowDefinition;
|
||||
import org.dromara.warm.flow.orm.entity.FlowHisTask;
|
||||
import org.dromara.warm.flow.orm.entity.FlowInstance;
|
||||
import org.dromara.workflow.common.enums.TaskStatusEnum;
|
||||
import org.dromara.workflow.domain.FlowInstanceBizExt;
|
||||
import org.dromara.workflow.domain.bo.FlowTaskBo;
|
||||
import org.dromara.workflow.domain.vo.FlowHisTaskVo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 任务信息Mapper接口
|
||||
*
|
||||
* @author may
|
||||
* @date 2024-03-02
|
||||
*/
|
||||
public interface FlwHisTaskMapper extends BaseMapperPlus<FlowHisTask, FlowHisTaskVo>, MPJBaseMapper<FlowHisTask> {
|
||||
|
||||
/**
|
||||
* 分页查询已办任务列表。
|
||||
*
|
||||
* @param page 分页对象
|
||||
* @param bo 查询条件
|
||||
* @param categoryIds 流程分类 ID 集合
|
||||
* @param userId 当前用户 ID
|
||||
* @return 已办任务分页结果
|
||||
*/
|
||||
default Page<FlowHisTaskVo> getListFinishTask(Page<FlowHisTaskVo> page, FlowTaskBo bo, List<String> categoryIds, String userId) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
return QueryBuilder.lambdaJoin("a", FlowHisTask.class)
|
||||
.select(FlowHisTask::getId, FlowHisTask::getNodeCode, FlowHisTask::getNodeName,
|
||||
FlowHisTask::getCooperateType, FlowHisTask::getApprover, FlowHisTask::getCollaborator,
|
||||
FlowHisTask::getNodeType, FlowHisTask::getTargetNodeCode, FlowHisTask::getTargetNodeName,
|
||||
FlowHisTask::getDefinitionId, FlowHisTask::getInstanceId)
|
||||
.selectAs(FlowHisTask::getFlowStatus, FlowHisTaskVo::getFlowTaskStatus)
|
||||
.select(FlowHisTask::getMessage, FlowHisTask::getExt, FlowHisTask::getCreateTime,
|
||||
FlowHisTask::getUpdateTime, FlowHisTask::getFormCustom, FlowHisTask::getFormPath)
|
||||
.select("b", FlowInstance::getFlowStatus, FlowInstance::getBusinessId, FlowInstance::getCreateBy)
|
||||
.select("c", FlowDefinition::getFlowName, FlowDefinition::getFlowCode, FlowDefinition::getCategory,
|
||||
FlowDefinition::getVersion)
|
||||
.select("biz", FlowInstanceBizExt::getBusinessCode, FlowInstanceBizExt::getBusinessTitle)
|
||||
.leftJoin(FlowInstance.class, "b", FlowInstance::getId, FlowHisTask::getInstanceId)
|
||||
.leftJoin(FlowDefinition.class, "c", FlowDefinition::getId, FlowHisTask::getDefinitionId)
|
||||
.leftJoin(FlowInstanceBizExt.class, "biz", FlowInstanceBizExt::getInstanceId, FlowInstance::getId)
|
||||
.in("a", FlowHisTask::getNodeType, List.of(1, 3, 4))
|
||||
.ne("a", FlowHisTask::getFlowStatus, TaskStatusEnum.COPY.getStatus())
|
||||
.likeIfText("a", FlowHisTask::getNodeName, bo.getNodeName())
|
||||
.likeIfText("c", FlowDefinition::getFlowName, bo.getFlowName())
|
||||
.likeIfText("c", FlowDefinition::getFlowCode, bo.getFlowCode())
|
||||
.likeIfText("b", FlowInstance::getFlowStatus, bo.getFlowStatus())
|
||||
.inIfNotEmpty("b", FlowInstance::getCreateBy, bo.getCreateByIds())
|
||||
.inIfNotEmpty("c", FlowDefinition::getCategory, categoryIds)
|
||||
.betweenParams("a", FlowHisTask::getCreateTime, params, "beginTime", "endTime")
|
||||
.eq(userId != null && !userId.isBlank(), "a", FlowHisTask::getNodeType, NodeType.BETWEEN.getKey())
|
||||
.eqIfText("a", FlowHisTask::getApprover, userId)
|
||||
.orderByDesc("a", FlowHisTask::getCreateTime)
|
||||
.orderByDesc("a", FlowHisTask::getUpdateTime)
|
||||
.page(page, FlowHisTaskVo.class);
|
||||
}
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package org.dromara.workflow.mapper;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.workflow.domain.FlowInstanceBizExt;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 流程实例业务扩展Mapper接口
|
||||
*
|
||||
* @author may
|
||||
* @date 2025-08-05
|
||||
*/
|
||||
public interface FlwInstanceBizExtMapper extends BaseMapperPlus<FlowInstanceBizExt, FlowInstanceBizExt> {
|
||||
|
||||
/**
|
||||
* 根据 instanceId 保存或更新流程实例业务扩展
|
||||
*
|
||||
* @param entity 流程实例业务扩展实体
|
||||
* @return 操作是否成功
|
||||
*/
|
||||
default int saveOrUpdateByInstanceId(FlowInstanceBizExt entity) {
|
||||
// 查询是否存在
|
||||
FlowInstanceBizExt exist = this.lambda()
|
||||
.eq(FlowInstanceBizExt::getInstanceId, entity.getInstanceId())
|
||||
.one();
|
||||
|
||||
if (ObjectUtil.isNotNull(exist)) {
|
||||
// 存在就带上主键更新
|
||||
entity.setId(exist.getId());
|
||||
return updateById(entity);
|
||||
} else {
|
||||
// 不存在就插入
|
||||
return insert(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照流程实例ID删除单个流程实例业务扩展
|
||||
*
|
||||
* @param instanceId 流程实例ID
|
||||
* @return 删除的行数
|
||||
*/
|
||||
default int deleteByInstId(Long instanceId) {
|
||||
return this.lambda().eq(FlowInstanceBizExt::getInstanceId, instanceId).deleteCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照流程实例ID批量删除流程实例业务扩展
|
||||
*
|
||||
* @param instanceIds 流程实例ID列表
|
||||
* @return 删除的行数
|
||||
*/
|
||||
default int deleteByInstIds(Collection<Long> instanceIds) {
|
||||
return this.lambda().in(FlowInstanceBizExt::getInstanceId, instanceIds).deleteCount();
|
||||
}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package org.dromara.workflow.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||
import org.dromara.warm.flow.orm.entity.FlowInstance;
|
||||
import org.dromara.workflow.domain.vo.FlowInstanceVo;
|
||||
|
||||
/**
|
||||
* 实例信息Mapper接口
|
||||
*
|
||||
* @author may
|
||||
* @date 2024-03-02
|
||||
*/
|
||||
public interface FlwInstanceMapper extends MPJBaseMapper<FlowInstance> {
|
||||
|
||||
/**
|
||||
* 流程实例信息
|
||||
*
|
||||
* @param page 分页
|
||||
* @param queryWrapper 条件
|
||||
* @return 结果
|
||||
*/
|
||||
default Page<FlowInstanceVo> selectInstanceList(Page<FlowInstanceVo> page, MPJLambdaWrapper<FlowInstance> queryWrapper) {
|
||||
return this.selectJoinPage(page, FlowInstanceVo.class, queryWrapper);
|
||||
}
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.dromara.workflow.mapper;
|
||||
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.workflow.domain.FlowSpel;
|
||||
import org.dromara.workflow.domain.vo.FlowSpelVo;
|
||||
|
||||
/**
|
||||
* 流程spel表达式定义Mapper接口
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
* @date 2025-07-04
|
||||
*/
|
||||
public interface FlwSpelMapper extends BaseMapperPlus<FlowSpel, FlowSpelVo> {
|
||||
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package org.dromara.workflow.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.dromara.common.core.enums.BusinessStatusEnum;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.common.mybatis.core.query.QueryBuilder;
|
||||
import org.dromara.warm.flow.core.enums.NodeType;
|
||||
import org.dromara.warm.flow.orm.entity.*;
|
||||
import org.dromara.workflow.domain.FlowInstanceBizExt;
|
||||
import org.dromara.workflow.domain.bo.FlowTaskBo;
|
||||
import org.dromara.workflow.domain.vo.FlowTaskVo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 任务信息Mapper接口
|
||||
*
|
||||
* @author may
|
||||
* @date 2024-03-02
|
||||
*/
|
||||
public interface FlwTaskMapper extends BaseMapperPlus<FlowTask, FlowTaskVo>, MPJBaseMapper<FlowTask> {
|
||||
|
||||
/**
|
||||
* 分页查询运行中的待办任务。
|
||||
*
|
||||
* @param page 分页对象
|
||||
* @param bo 查询条件
|
||||
* @param categoryIds 流程分类 id 列表
|
||||
* @param userId 当前用户 id
|
||||
* @return 待办任务分页结果
|
||||
*/
|
||||
default Page<FlowTaskVo> getListRunTask(Page<FlowTaskVo> page, FlowTaskBo bo, List<String> categoryIds, String userId) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
return QueryBuilder.lambdaJoin("t", FlowTask.class)
|
||||
.distinct()
|
||||
.select(FlowTask::getId, FlowTask::getNodeCode, FlowTask::getNodeName, FlowTask::getNodeType,
|
||||
FlowTask::getDefinitionId, FlowTask::getInstanceId, FlowTask::getCreateTime, FlowTask::getUpdateTime)
|
||||
.select("i", FlowInstance::getBusinessId, FlowInstance::getFlowStatus, FlowInstance::getCreateBy)
|
||||
.select("d", FlowDefinition::getFlowName, FlowDefinition::getFlowCode, FlowDefinition::getFormCustom,
|
||||
FlowDefinition::getCategory, FlowDefinition::getVersion)
|
||||
.select("uu", FlowUser::getProcessedBy, FlowUser::getType)
|
||||
.select("biz", FlowInstanceBizExt::getBusinessCode, FlowInstanceBizExt::getBusinessTitle)
|
||||
.selectAs("COALESCE(NULLIF(TRIM(t.form_path), ''), NULLIF(TRIM(d.form_path), ''))", FlowTaskVo::getFormPath)
|
||||
.leftJoin(FlowUser.class, "uu", FlowUser::getAssociated, FlowTask::getId)
|
||||
.leftJoin(FlowDefinition.class, "d", FlowDefinition::getId, FlowTask::getDefinitionId)
|
||||
.leftJoin(FlowInstance.class, "i", FlowInstance::getId, FlowTask::getInstanceId)
|
||||
.leftJoin(FlowInstanceBizExt.class, "biz", FlowInstanceBizExt::getInstanceId, FlowInstance::getId)
|
||||
.eq("t", FlowTask::getNodeType, NodeType.BETWEEN.getKey())
|
||||
.in("uu", FlowUser::getType, List.of("1", "2", "3"))
|
||||
.likeIfText("t", FlowTask::getNodeName, bo.getNodeName())
|
||||
.likeIfText("d", FlowDefinition::getFlowName, bo.getFlowName())
|
||||
.likeIfText("d", FlowDefinition::getFlowCode, bo.getFlowCode())
|
||||
.likeIfText("i", FlowInstance::getFlowStatus, bo.getFlowStatus())
|
||||
.inIfNotEmpty("i", FlowInstance::getCreateBy, bo.getCreateByIds())
|
||||
.inIfNotEmpty("d", FlowDefinition::getCategory, categoryIds)
|
||||
.betweenParams("t", FlowTask::getCreateTime, params, "beginTime", "endTime")
|
||||
.eqIfText("uu", FlowUser::getProcessedBy, userId)
|
||||
.eq(userId != null && !userId.isBlank(), "i", FlowInstance::getFlowStatus, BusinessStatusEnum.WAITING.getStatus())
|
||||
.orderByDesc("t", FlowTask::getCreateTime)
|
||||
.orderByDesc("t", FlowTask::getUpdateTime)
|
||||
.page(page, FlowTaskVo.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询抄送任务。
|
||||
*
|
||||
* @param page 分页对象
|
||||
* @param bo 查询条件
|
||||
* @param categoryIds 流程分类 id 列表
|
||||
* @param userId 当前用户 id
|
||||
* @return 抄送任务分页结果
|
||||
*/
|
||||
default Page<FlowTaskVo> getTaskCopyByPage(Page<FlowTaskVo> page, FlowTaskBo bo, List<String> categoryIds, String userId) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
return QueryBuilder.lambdaJoin("a", FlowUser.class)
|
||||
.select(FlowUser::getProcessedBy, FlowUser::getCreateTime)
|
||||
.select("b", FlowHisTask::getId, FlowHisTask::getUpdateTime, FlowHisTask::getFormCustom,
|
||||
FlowHisTask::getFormPath, FlowHisTask::getNodeName, FlowHisTask::getNodeCode)
|
||||
.select("c", FlowInstance::getBusinessId, FlowInstance::getFlowStatus, FlowInstance::getCreateBy)
|
||||
.select("d", FlowDefinition::getFlowName, FlowDefinition::getFlowCode, FlowDefinition::getCategory,
|
||||
FlowDefinition::getVersion)
|
||||
.select("biz", FlowInstanceBizExt::getBusinessCode, FlowInstanceBizExt::getBusinessTitle)
|
||||
.leftJoin(FlowHisTask.class, "b", FlowHisTask::getTaskId, FlowUser::getAssociated)
|
||||
.leftJoin(FlowInstance.class, "c", FlowInstance::getId, FlowHisTask::getInstanceId)
|
||||
.leftJoin(FlowDefinition.class, "d", FlowDefinition::getId, FlowInstance::getDefinitionId)
|
||||
.leftJoin(FlowInstanceBizExt.class, "biz", FlowInstanceBizExt::getInstanceId, FlowInstance::getId)
|
||||
.eq("a", FlowUser::getType, "4")
|
||||
.likeIfText("b", FlowHisTask::getNodeName, bo.getNodeName())
|
||||
.likeIfText("d", FlowDefinition::getFlowName, bo.getFlowName())
|
||||
.likeIfText("d", FlowDefinition::getFlowCode, bo.getFlowCode())
|
||||
.likeIfText("c", FlowInstance::getFlowStatus, bo.getFlowStatus())
|
||||
.inIfNotEmpty("c", FlowInstance::getCreateBy, bo.getCreateByIds())
|
||||
.inIfNotEmpty("d", FlowDefinition::getCategory, categoryIds)
|
||||
.betweenParams("a", FlowUser::getCreateTime, params, "beginTime", "endTime")
|
||||
.eqIfText("a", FlowUser::getProcessedBy, userId)
|
||||
.orderByDesc("a", FlowUser::getCreateTime)
|
||||
.orderByDesc("b", FlowHisTask::getUpdateTime)
|
||||
.page(page, FlowTaskVo.class);
|
||||
}
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.dromara.workflow.mapper;
|
||||
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.warm.flow.orm.entity.FlowUser;
|
||||
|
||||
/**
|
||||
* 任务信息Mapper接口
|
||||
*
|
||||
* @author may
|
||||
* @date 2024-03-02
|
||||
*/
|
||||
public interface FlwUserMapper extends BaseMapperPlus<FlowUser, FlowUser>, MPJBaseMapper<FlowUser> {
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.dromara.workflow.mapper;
|
||||
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.workflow.domain.TestLeave;
|
||||
import org.dromara.workflow.domain.vo.TestLeaveVo;
|
||||
|
||||
/**
|
||||
* 请假Mapper接口
|
||||
*
|
||||
* @author may
|
||||
* @date 2023-07-21
|
||||
*/
|
||||
public interface TestLeaveMapper extends BaseMapperPlus<TestLeave, TestLeaveVo> {
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package org.dromara.workflow.rule;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.system.api.DeptService;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* spel表达式规则组件
|
||||
* <p>
|
||||
* 通过该组件统一管理流程定义中的spel表达式
|
||||
* </p>
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SpelRuleComponent {
|
||||
|
||||
private final DeptService deptService;
|
||||
|
||||
/**
|
||||
* 通过发起人部门 ID 获取部门负责人。
|
||||
*
|
||||
* @param initiatorDeptId 发起人部门 ID
|
||||
* @return 部门负责人用户 ID
|
||||
*/
|
||||
public Long selectDeptLeaderById(Long initiatorDeptId) {
|
||||
Long leaderId = deptService.selectDeptLeaderById(initiatorDeptId);
|
||||
if (ObjectUtil.isNull(leaderId)) {
|
||||
throw new ServiceException("当前部门未设置负责人,请联系管理员操作。");
|
||||
}
|
||||
return leaderId;
|
||||
}
|
||||
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package org.dromara.workflow.service;
|
||||
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import org.dromara.workflow.domain.bo.FlowCategoryBo;
|
||||
import org.dromara.workflow.domain.vo.FlowCategoryVo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 流程分类Service接口
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
public interface IFlwCategoryService {
|
||||
|
||||
/**
|
||||
* 查询流程分类
|
||||
*
|
||||
* @param categoryId 主键
|
||||
* @return 流程分类
|
||||
*/
|
||||
FlowCategoryVo queryById(Long categoryId);
|
||||
|
||||
/**
|
||||
* 根据流程分类ID查询流程分类名称
|
||||
*
|
||||
* @param categoryId 流程分类ID
|
||||
* @return 流程分类名称
|
||||
*/
|
||||
String selectCategoryNameById(Long categoryId);
|
||||
|
||||
/**
|
||||
* 根据流程分类ID查询流程分类名称
|
||||
*
|
||||
* @param categoryIds 流程分类ID
|
||||
* @return 流程分类名称
|
||||
*/
|
||||
Map<Long, String> selectCategoryNameByIds(Set<Long> categoryIds);
|
||||
|
||||
/**
|
||||
* 查询符合条件的流程分类列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 流程分类列表
|
||||
*/
|
||||
List<FlowCategoryVo> queryList(FlowCategoryBo bo);
|
||||
|
||||
/**
|
||||
* 查询流程分类树结构信息
|
||||
*
|
||||
* @param category 流程分类信息
|
||||
* @return 流程分类树信息集合
|
||||
*/
|
||||
List<Tree<String>> selectCategoryTreeList(FlowCategoryBo category);
|
||||
|
||||
/**
|
||||
* 校验流程分类名称是否唯一
|
||||
*
|
||||
* @param category 流程分类信息
|
||||
* @return 结果
|
||||
*/
|
||||
boolean checkCategoryNameUnique(FlowCategoryBo category);
|
||||
|
||||
/**
|
||||
* 查询流程分类是否存在流程定义
|
||||
*
|
||||
* @param categoryId 流程分类ID
|
||||
* @return 结果 true 存在 false 不存在
|
||||
*/
|
||||
boolean checkCategoryExistDefinition(Long categoryId);
|
||||
|
||||
/**
|
||||
* 是否存在流程分类子节点
|
||||
*
|
||||
* @param categoryId 流程分类ID
|
||||
* @return 结果
|
||||
*/
|
||||
boolean hasChildByCategoryId(Long categoryId);
|
||||
|
||||
/**
|
||||
* 新增流程分类
|
||||
*
|
||||
* @param bo 流程分类
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
int insertByBo(FlowCategoryBo bo);
|
||||
|
||||
/**
|
||||
* 修改流程分类
|
||||
*
|
||||
* @param bo 流程分类
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
int updateByBo(FlowCategoryBo bo);
|
||||
|
||||
/**
|
||||
* 删除流程分类信息
|
||||
*
|
||||
* @param categoryId 主键
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
int deleteWithValidById(Long categoryId);
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package org.dromara.workflow.service;
|
||||
|
||||
import org.dromara.common.core.enums.BusinessStatusEnum;
|
||||
import org.dromara.system.api.domain.UserDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 通用 工作流服务
|
||||
*
|
||||
* @author LionLi
|
||||
*/
|
||||
public interface IFlwCommonService {
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param flowName 流程定义名称
|
||||
* @param instId 实例id
|
||||
* @param messageType 消息类型
|
||||
* @param message 消息内容,为空则发送默认配置的消息内容
|
||||
*/
|
||||
void sendMessage(String flowName, Long instId, List<String> messageType, String message);
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param messageType 消息类型
|
||||
* @param message 消息内容
|
||||
* @param subject 邮件标题
|
||||
* @param userList 接收用户
|
||||
*/
|
||||
void sendMessage(List<String> messageType, String message, String subject, List<UserDTO> userList);
|
||||
|
||||
/**
|
||||
* 发送带跳转路径的消息。
|
||||
*
|
||||
* @param messageType 消息类型
|
||||
* @param message 消息内容
|
||||
* @param subject 邮件标题
|
||||
* @param userList 接收用户
|
||||
* @param path 前端跳转路径
|
||||
*/
|
||||
void sendMessage(List<String> messageType, String message, String subject, List<UserDTO> userList, String path);
|
||||
|
||||
/**
|
||||
* 发送工作流结果消息给指定用户。
|
||||
*
|
||||
* @param flowName 流程名称
|
||||
* @param status 流程状态
|
||||
* @param messageType 消息类型
|
||||
* @param userList 接收用户
|
||||
*/
|
||||
void sendResultMessage(String flowName, BusinessStatusEnum status, List<String> messageType, List<UserDTO> userList);
|
||||
|
||||
/**
|
||||
* 申请人节点编码
|
||||
*
|
||||
* @param definitionId 流程定义id
|
||||
* @return 申请人节点编码
|
||||
*/
|
||||
String applyNodeCode(Long definitionId);
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package org.dromara.workflow.service;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.warm.flow.orm.entity.FlowDefinition;
|
||||
import org.dromara.workflow.domain.vo.FlowDefinitionVo;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 流程定义 服务层
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
public interface IFlwDefinitionService {
|
||||
|
||||
/**
|
||||
* 查询流程定义列表
|
||||
*
|
||||
* @param flowDefinition 参数
|
||||
* @param pageQuery 分页
|
||||
* @return 返回分页列表
|
||||
*/
|
||||
PageResult<FlowDefinitionVo> queryList(FlowDefinition flowDefinition, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询未发布的流程定义列表
|
||||
*
|
||||
* @param flowDefinition 参数
|
||||
* @param pageQuery 分页
|
||||
* @return 返回分页列表
|
||||
*/
|
||||
PageResult<FlowDefinitionVo> unPublishList(FlowDefinition flowDefinition, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 发布流程定义
|
||||
*
|
||||
* @param id 流程定义id
|
||||
* @return 是否发布成功
|
||||
*/
|
||||
boolean publish(Long id);
|
||||
|
||||
/**
|
||||
* 导出流程定义
|
||||
*
|
||||
* @param id 流程定义id
|
||||
* @param response 响应
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
void exportDef(Long id, HttpServletResponse response) throws IOException;
|
||||
|
||||
/**
|
||||
* 导入流程定义
|
||||
*
|
||||
* @param file 文件
|
||||
* @param category 分类
|
||||
* @return 是否导入成功
|
||||
*/
|
||||
boolean importJson(MultipartFile file, String category);
|
||||
|
||||
/**
|
||||
* 删除流程定义
|
||||
*
|
||||
* @param ids 流程定义id
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
boolean removeDef(Collection<Long> ids);
|
||||
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
package org.dromara.workflow.service;
|
||||
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.warm.flow.orm.entity.FlowInstance;
|
||||
import org.dromara.workflow.domain.bo.FlowCancelBo;
|
||||
import org.dromara.workflow.domain.bo.FlowInstanceBo;
|
||||
import org.dromara.workflow.domain.bo.FlowInvalidBo;
|
||||
import org.dromara.workflow.domain.bo.FlowVariableBo;
|
||||
import org.dromara.workflow.domain.vo.FlowInstanceVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 流程实例 服务层
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
public interface IFlwInstanceService {
|
||||
|
||||
/**
|
||||
* 分页查询正在运行的流程实例
|
||||
*
|
||||
* @param flowInstanceBo 流程实例
|
||||
* @param pageQuery 分页
|
||||
* @return 结果
|
||||
*/
|
||||
PageResult<FlowInstanceVo> selectRunningInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 分页查询已结束的流程实例
|
||||
*
|
||||
* @param flowInstanceBo 流程实例
|
||||
* @param pageQuery 分页
|
||||
* @return 结果
|
||||
*/
|
||||
PageResult<FlowInstanceVo> selectFinishInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 根据业务 id 查询流程实例详情,返回业务侧展示所需信息。
|
||||
*
|
||||
* @param businessId 业务id
|
||||
* @return 结果
|
||||
*/
|
||||
FlowInstanceVo queryByBusinessId(Long businessId);
|
||||
|
||||
/**
|
||||
* 按业务 id 查询底层流程实例实体。
|
||||
*
|
||||
* @param businessId 业务id
|
||||
* @return 结果
|
||||
*/
|
||||
FlowInstance selectInstByBusinessId(String businessId);
|
||||
|
||||
/**
|
||||
* 按实例 id 查询单个流程实例实体。
|
||||
*
|
||||
* @param instanceId 实例id
|
||||
* @return 结果
|
||||
*/
|
||||
FlowInstance selectInstById(Long instanceId);
|
||||
|
||||
/**
|
||||
* 按实例 id 集合批量查询流程实例。
|
||||
*
|
||||
* @param instanceIds 实例id
|
||||
* @return 结果
|
||||
*/
|
||||
List<FlowInstance> selectInstListByIdList(Collection<Long> instanceIds);
|
||||
|
||||
/**
|
||||
* 按照业务id删除流程实例
|
||||
*
|
||||
* @param businessIds 业务id
|
||||
* @return 结果
|
||||
*/
|
||||
boolean deleteByBusinessIds(List<String> businessIds);
|
||||
|
||||
/**
|
||||
* 按照实例id删除流程实例
|
||||
*
|
||||
* @param instanceIds 实例id
|
||||
* @return 结果
|
||||
*/
|
||||
boolean deleteByInstanceIds(Collection<Long> instanceIds);
|
||||
|
||||
/**
|
||||
* 按照实例id删除已完成得流程实例
|
||||
*
|
||||
* @param instanceIds 删除的实例id
|
||||
* @return 删除结果
|
||||
*/
|
||||
boolean deleteHisByInstanceIds(Collection<Long> instanceIds);
|
||||
|
||||
/**
|
||||
* 撤销流程
|
||||
*
|
||||
* @param bo 参数
|
||||
* @return 结果
|
||||
*/
|
||||
boolean cancelProcessApply(FlowCancelBo bo);
|
||||
|
||||
/**
|
||||
* 获取当前登陆人发起的流程实例
|
||||
*
|
||||
* @param instanceBo 流程实例
|
||||
* @param pageQuery 分页
|
||||
* @return 结果
|
||||
*/
|
||||
PageResult<FlowInstanceVo> selectCurrentInstanceList(FlowInstanceBo instanceBo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 获取流程图及历史办理记录,供前端展示流转轨迹。
|
||||
*
|
||||
* @param businessId 业务id
|
||||
* @return 结果
|
||||
*/
|
||||
Map<String, Object> flowHisTaskList(String businessId);
|
||||
|
||||
/**
|
||||
* 按照实例id更新状态
|
||||
*
|
||||
* @param instanceId 实例id
|
||||
* @param status 状态
|
||||
*/
|
||||
void updateStatus(Long instanceId, String status);
|
||||
|
||||
/**
|
||||
* 获取指定实例当前持有的流程变量。
|
||||
*
|
||||
* @param instanceId 实例id
|
||||
* @return 结果
|
||||
*/
|
||||
Map<String, Object> instanceVariable(Long instanceId);
|
||||
|
||||
/**
|
||||
* 更新流程变量,支持在运行中修正流程上下文数据。
|
||||
*
|
||||
* @param bo 参数
|
||||
* @return 结果
|
||||
*/
|
||||
boolean updateVariable(FlowVariableBo bo);
|
||||
|
||||
/**
|
||||
* 为指定流程实例设置变量集合。
|
||||
*
|
||||
* @param instanceId 实例id
|
||||
* @param variable 流程变量
|
||||
*/
|
||||
void setVariable(Long instanceId, Map<String, Object> variable);
|
||||
|
||||
/**
|
||||
* 按任务 id 反查所属流程实例。
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @return 结果
|
||||
*/
|
||||
FlowInstance selectByTaskId(Long taskId);
|
||||
|
||||
/**
|
||||
* 作废流程
|
||||
*
|
||||
* @param bo 流程实例
|
||||
* @return 结果
|
||||
*/
|
||||
boolean processInvalid(FlowInvalidBo bo);
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package org.dromara.workflow.service;
|
||||
|
||||
import org.dromara.workflow.domain.vo.NodeExtVo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 流程节点扩展属性 服务层
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
public interface IFlwNodeExtService {
|
||||
|
||||
/**
|
||||
* 解析扩展属性 JSON 并构建 Node 扩展属性对象
|
||||
* <p>
|
||||
* 根据传入的 JSON 字符串,将扩展属性分为三类:
|
||||
* 1. ButtonPermissionEnum:解析为按钮权限列表,标记每个按钮是否勾选
|
||||
* 2. CopySettingEnum:解析为抄送对象 ID 集合
|
||||
* 3. VariablesEnum:解析为自定义参数 Map
|
||||
*
|
||||
* <p>示例 JSON:
|
||||
* [
|
||||
* {"code": "ButtonPermissionEnum", "value": "back,termination"},
|
||||
* {"code": "CopySettingEnum", "value": "1"},
|
||||
* {"code": "VariablesEnum", "value": "key1=value1,key2=value2"}
|
||||
* ]
|
||||
*
|
||||
* @param ext 扩展属性 JSON 字符串
|
||||
* @param variable 流程变量
|
||||
* @return NodeExtVo 对象,封装按钮权限列表、抄送对象集合和自定义参数 Map
|
||||
*/
|
||||
NodeExtVo parseNodeExt(String ext, Map<String, Object> variable);
|
||||
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package org.dromara.workflow.service;
|
||||
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.system.api.domain.TaskAssigneeDTO;
|
||||
import org.dromara.system.api.model.TaskAssigneeBody;
|
||||
import org.dromara.workflow.domain.bo.FlowSpelBo;
|
||||
import org.dromara.workflow.domain.vo.FlowSpelVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 流程 SpEL 表达式定义服务接口,负责表达式规则管理与动态指派解析。
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
* @date 2025-07-04
|
||||
*/
|
||||
public interface IFlwSpelService {
|
||||
|
||||
/**
|
||||
* 根据主键查询流程 SpEL 表达式定义。
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 流程spel表达式定义
|
||||
*/
|
||||
FlowSpelVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询流程spel表达式定义列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 流程spel表达式定义分页列表
|
||||
*/
|
||||
PageResult<FlowSpelVo> queryPageList(FlowSpelBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询符合条件的流程 SpEL 表达式定义列表。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 流程spel表达式定义列表
|
||||
*/
|
||||
List<FlowSpelVo> queryList(FlowSpelBo bo);
|
||||
|
||||
/**
|
||||
* 新增流程 SpEL 表达式定义。
|
||||
*
|
||||
* @param bo 流程spel表达式定义
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
Boolean insertByBo(FlowSpelBo bo);
|
||||
|
||||
/**
|
||||
* 修改流程 SpEL 表达式定义。
|
||||
*
|
||||
* @param bo 流程spel表达式定义
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean updateByBo(FlowSpelBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除流程spel表达式定义信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 按任务指派条件解析 SpEL,并返回可用办理人列表。
|
||||
*
|
||||
* @param taskQuery 查询条件
|
||||
* @return 办理人
|
||||
*/
|
||||
TaskAssigneeDTO selectSpelByTaskAssigneeList(TaskAssigneeBody taskQuery);
|
||||
|
||||
/**
|
||||
* 根据视图 SpEL 表达式列表,查询对应的备注信息
|
||||
*
|
||||
* @param viewSpels SpEL 表达式列表
|
||||
* @return 映射表:key 为 SpEL 表达式,value 为对应备注;若为空则返回空 Map
|
||||
*/
|
||||
Map<String, String> selectRemarksBySpels(List<String> viewSpels);
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package org.dromara.workflow.service;
|
||||
|
||||
import org.dromara.system.api.domain.UserDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程设计器-获取办理人
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
public interface IFlwTaskAssigneeService {
|
||||
|
||||
/**
|
||||
* 批量解析多个存储标识符(storageIds),按类型分类并合并查询用户列表
|
||||
* 输入格式支持多个以逗号分隔的标识(如 "user:123,role:456,789")
|
||||
* 会自动去重返回结果,非法格式的标识将被忽略
|
||||
*
|
||||
* @param storageIds 多个存储标识符字符串(逗号分隔)
|
||||
* @return 合并后的用户列表,去重后返回,非法格式的标识将被跳过
|
||||
*/
|
||||
List<UserDTO> fetchUsersByStorageIds(String storageIds);
|
||||
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
package org.dromara.workflow.service;
|
||||
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.system.api.domain.UserDTO;
|
||||
import org.dromara.warm.flow.core.entity.Node;
|
||||
import org.dromara.warm.flow.core.entity.Task;
|
||||
import org.dromara.warm.flow.orm.entity.FlowHisTask;
|
||||
import org.dromara.warm.flow.orm.entity.FlowNode;
|
||||
import org.dromara.warm.flow.orm.entity.FlowTask;
|
||||
import org.dromara.workflow.api.domain.StartProcessReturnDTO;
|
||||
import org.dromara.workflow.domain.bo.*;
|
||||
import org.dromara.workflow.domain.vo.FlowHisTaskVo;
|
||||
import org.dromara.workflow.domain.vo.FlowTaskVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 任务 服务层
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
public interface IFlwTaskService {
|
||||
|
||||
/**
|
||||
* 启动任务
|
||||
*
|
||||
* @param startProcessBo 启动流程参数
|
||||
* @return 结果
|
||||
*/
|
||||
StartProcessReturnDTO startWorkFlow(StartProcessBo startProcessBo);
|
||||
|
||||
/**
|
||||
* 办理任务
|
||||
*
|
||||
* @param completeTaskBo 办理任务参数
|
||||
* @return 结果
|
||||
*/
|
||||
boolean completeTask(CompleteTaskBo completeTaskBo);
|
||||
|
||||
/**
|
||||
* 添加抄送人
|
||||
*
|
||||
* @param task 任务信息
|
||||
* @param flowCopyList 抄送人
|
||||
*/
|
||||
void setCopy(Task task, List<FlowCopyBo> flowCopyList);
|
||||
|
||||
/**
|
||||
* 查询当前用户的待办任务
|
||||
*
|
||||
* @param flowTaskBo 参数
|
||||
* @param pageQuery 分页
|
||||
* @return 当前用户待办任务分页结果
|
||||
*/
|
||||
PageResult<FlowTaskVo> pageByTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询当前租户所有待办任务
|
||||
*
|
||||
* @param flowTaskBo 参数
|
||||
* @param pageQuery 分页
|
||||
* @return 当前用户已办任务分页结果
|
||||
*/
|
||||
PageResult<FlowHisTaskVo> pageByTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询待办任务
|
||||
*
|
||||
* @param flowTaskBo 参数
|
||||
* @param pageQuery 分页
|
||||
* @return 当前租户待办任务分页结果
|
||||
*/
|
||||
PageResult<FlowTaskVo> pageByAllTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询已办任务
|
||||
*
|
||||
* @param flowTaskBo 参数
|
||||
* @param pageQuery 分页
|
||||
* @return 当前租户已办任务分页结果
|
||||
*/
|
||||
PageResult<FlowHisTaskVo> pageByAllTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询当前用户的抄送
|
||||
*
|
||||
* @param flowTaskBo 参数
|
||||
* @param pageQuery 分页
|
||||
* @return 当前用户抄送分页结果
|
||||
*/
|
||||
PageResult<FlowTaskVo> pageByTaskCopy(FlowTaskBo flowTaskBo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 修改任务办理人
|
||||
*
|
||||
* @param taskIdList 任务id
|
||||
* @param userId 用户id
|
||||
* @return 结果
|
||||
*/
|
||||
boolean updateAssignee(Collection<Long> taskIdList, String userId);
|
||||
|
||||
/**
|
||||
* 驳回审批
|
||||
*
|
||||
* @param bo 参数
|
||||
* @return 结果
|
||||
*/
|
||||
boolean backProcess(BackProcessBo bo);
|
||||
|
||||
/**
|
||||
* 获取可驳回的前置节点
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @param nowNodeCode 当前节点
|
||||
* @return 结果
|
||||
*/
|
||||
List<Node> getBackTaskNode(Long taskId, String nowNodeCode);
|
||||
|
||||
/**
|
||||
* 终止任务
|
||||
*
|
||||
* @param bo 参数
|
||||
* @return 结果
|
||||
*/
|
||||
boolean terminationTask(FlowTerminationBo bo);
|
||||
|
||||
/**
|
||||
* 按照任务id查询任务
|
||||
*
|
||||
* @param taskIdList 任务id
|
||||
* @return 任务列表
|
||||
*/
|
||||
List<FlowTask> selectByIdList(Collection<Long> taskIdList);
|
||||
|
||||
/**
|
||||
* 按照任务id查询任务
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @return 任务详情
|
||||
*/
|
||||
FlowTaskVo selectById(Long taskId);
|
||||
|
||||
/**
|
||||
* 获取下一节点信息
|
||||
*
|
||||
* @param bo 参数
|
||||
* @return 下一节点列表
|
||||
*/
|
||||
List<FlowNode> getNextNodeList(FlowNextNodeBo bo);
|
||||
|
||||
/**
|
||||
* 按照任务id查询任务
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @return 历史任务详情
|
||||
*/
|
||||
FlowHisTask selectHisTaskById(Long taskId);
|
||||
|
||||
/**
|
||||
* 按照实例id查询任务
|
||||
*
|
||||
* @param instanceId 流程实例id
|
||||
* @return 任务列表
|
||||
*/
|
||||
List<FlowTask> selectByInstId(Long instanceId);
|
||||
|
||||
/**
|
||||
* 按照实例id查询任务
|
||||
*
|
||||
* @param instanceIds 列表
|
||||
* @return 任务列表
|
||||
*/
|
||||
List<FlowTask> selectByInstIds(Collection<Long> instanceIds);
|
||||
|
||||
/**
|
||||
* 判断流程是否已结束(即该流程实例下是否还有未完成的任务)
|
||||
*
|
||||
* @param instanceId 流程实例ID
|
||||
* @return true 表示任务已全部结束;false 表示仍有任务存在
|
||||
*/
|
||||
boolean isTaskEnd(Long instanceId);
|
||||
|
||||
/**
|
||||
* 任务操作
|
||||
*
|
||||
* @param bo 参数
|
||||
* @param taskOperation 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature
|
||||
* @return 结果
|
||||
*/
|
||||
boolean taskOperation(TaskOperationBo bo, String taskOperation);
|
||||
|
||||
/**
|
||||
* 获取当前任务的所有办理人
|
||||
*
|
||||
* @param taskIds 任务id
|
||||
* @return 办理人列表
|
||||
*/
|
||||
List<UserDTO> currentTaskAllUser(List<Long> taskIds);
|
||||
|
||||
/**
|
||||
* 按照节点编码查询节点
|
||||
*
|
||||
* @param nodeCode 节点编码
|
||||
* @param definitionId 流程定义id
|
||||
* @return 节点
|
||||
*/
|
||||
FlowNode getByNodeCode(String nodeCode, Long definitionId);
|
||||
|
||||
/**
|
||||
* 催办任务
|
||||
*
|
||||
* @param bo 参数
|
||||
* @return 是否催办成功
|
||||
*/
|
||||
boolean urgeTask(FlowUrgeTaskBo bo);
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package org.dromara.workflow.service;
|
||||
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.workflow.domain.bo.TestLeaveBo;
|
||||
import org.dromara.workflow.domain.vo.TestLeaveVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 请假示例服务接口,定义请假单查询、维护与流程发起能力。
|
||||
*
|
||||
* @author may
|
||||
* @date 2023-07-21
|
||||
*/
|
||||
public interface ITestLeaveService {
|
||||
|
||||
/**
|
||||
* 根据主键查询请假单。
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 请假详情
|
||||
*/
|
||||
TestLeaveVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询请假列表。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 请假分页数据
|
||||
*/
|
||||
PageResult<TestLeaveVo> queryPageList(TestLeaveBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询符合条件的请假列表。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 请假列表
|
||||
*/
|
||||
List<TestLeaveVo> queryList(TestLeaveBo bo);
|
||||
|
||||
/**
|
||||
* 新增请假单。
|
||||
*
|
||||
* @param bo 请假信息
|
||||
* @return 新增结果
|
||||
*/
|
||||
TestLeaveVo insertByBo(TestLeaveBo bo);
|
||||
|
||||
/**
|
||||
* 提交请假单并发起审批流程。
|
||||
*
|
||||
* @param bo 请假信息
|
||||
* @return 提交结果
|
||||
*/
|
||||
TestLeaveVo submitAndFlowStart(TestLeaveBo bo);
|
||||
|
||||
/**
|
||||
* 修改请假单。
|
||||
*
|
||||
* @param bo 请假信息
|
||||
* @return 修改结果
|
||||
*/
|
||||
TestLeaveVo updateByBo(TestLeaveBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除请假信息。
|
||||
*
|
||||
* @param ids 主键集合
|
||||
* @return 删除结果
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids);
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package org.dromara.workflow.service.impl;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.translation.annotation.TranslationType;
|
||||
import org.dromara.common.translation.core.TranslationInterface;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.common.constant.FlowConstant;
|
||||
import org.dromara.workflow.service.IFlwCategoryService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 流程分类名称翻译实现
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
@TranslationType(type = FlowConstant.CATEGORY_ID_TO_NAME)
|
||||
public class CategoryNameTranslationImpl implements TranslationInterface<String> {
|
||||
|
||||
private final IFlwCategoryService flwCategoryService;
|
||||
|
||||
/**
|
||||
* 将流程分类 ID 翻译为分类名称。
|
||||
*
|
||||
* @param key 流程分类 ID
|
||||
* @param other 额外参数
|
||||
* @return 流程分类名称
|
||||
*/
|
||||
@Override
|
||||
public String translation(Object key, String other) {
|
||||
return flwCategoryService.selectCategoryNameById(Convert.toLong(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量将流程分类 ID 翻译为分类名称。
|
||||
*
|
||||
* @param keys 流程分类 ID 集合
|
||||
* @param other 额外参数
|
||||
* @return 流程分类 ID 与分类名称映射
|
||||
*/
|
||||
@Override
|
||||
public Map<Object, String> translationBatch(Set<Object> keys, String other) {
|
||||
Set<Long> categoryIds = collectLongIds(keys);
|
||||
if (categoryIds.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
Map<Long, String> categoryNames = flwCategoryService.selectCategoryNameByIds(categoryIds);
|
||||
Map<Object, String> result = new LinkedHashMap<>(keys.size());
|
||||
for (Object key : keys) {
|
||||
result.put(key, buildValue(key, categoryNames));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据原始键构建流程分类名称翻译值。
|
||||
*
|
||||
* @param source 原始键
|
||||
* @param categoryNames 流程分类 ID 与分类名称映射
|
||||
* @return 流程分类名称
|
||||
*/
|
||||
private String buildValue(Object source, Map<Long, String> categoryNames) {
|
||||
if (source instanceof String ids) {
|
||||
return joinMappedValues(ids, categoryNames::get);
|
||||
}
|
||||
return source == null ? null : categoryNames.get(Convert.toLong(source));
|
||||
}
|
||||
|
||||
}
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
package org.dromara.workflow.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.constant.SystemConstants;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.*;
|
||||
import org.dromara.warm.flow.core.service.DefService;
|
||||
import org.dromara.warm.flow.orm.entity.FlowDefinition;
|
||||
import org.dromara.warm.flow.ui.service.CategoryService;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.common.constant.FlowConstant;
|
||||
import org.dromara.workflow.domain.FlowCategory;
|
||||
import org.dromara.workflow.domain.bo.FlowCategoryBo;
|
||||
import org.dromara.workflow.domain.vo.FlowCategoryVo;
|
||||
import org.dromara.workflow.mapper.FlwCategoryMapper;
|
||||
import org.dromara.workflow.service.IFlwCategoryService;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 流程分类Service业务层处理
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryService {
|
||||
|
||||
private final DefService defService;
|
||||
private final FlwCategoryMapper categoryMapper;
|
||||
|
||||
/**
|
||||
* 查询流程分类
|
||||
*
|
||||
* @param categoryId 主键
|
||||
* @return 流程分类
|
||||
*/
|
||||
@Override
|
||||
public FlowCategoryVo queryById(Long categoryId) {
|
||||
return categoryMapper.selectVoById(categoryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据流程分类ID查询流程分类名称
|
||||
*
|
||||
* @param categoryId 流程分类ID
|
||||
* @return 流程分类名称
|
||||
*/
|
||||
@Cacheable(cacheNames = FlowConstant.FLOW_CATEGORY_NAME, key = "#categoryId")
|
||||
@Override
|
||||
public String selectCategoryNameById(Long categoryId) {
|
||||
if (ObjectUtil.isNull(categoryId)) {
|
||||
return null;
|
||||
}
|
||||
FlowCategory category = categoryMapper.lambda()
|
||||
.select(FlowCategory::getCategoryName)
|
||||
.eq(FlowCategory::getCategoryId, categoryId)
|
||||
.one();
|
||||
return ObjectUtils.notNullGetter(category, FlowCategory::getCategoryName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据流程分类ID查询流程分类名称
|
||||
*
|
||||
* @param categoryIds 流程分类ID
|
||||
* @return 流程分类名称
|
||||
*/
|
||||
@Override
|
||||
public Map<Long, String> selectCategoryNameByIds(Set<Long> categoryIds) {
|
||||
if (CollUtil.isEmpty(categoryIds)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<FlowCategory> list = categoryMapper.lambda()
|
||||
.select(FlowCategory::getCategoryId, FlowCategory::getCategoryName)
|
||||
.in(FlowCategory::getCategoryId, categoryIds)
|
||||
.list();
|
||||
return StreamUtils.toMap(list, FlowCategory::getCategoryId, FlowCategory::getCategoryName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询符合条件的流程分类列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 流程分类列表
|
||||
*/
|
||||
@Override
|
||||
public List<FlowCategoryVo> queryList(FlowCategoryBo bo) {
|
||||
LambdaQueryWrapper<FlowCategory> lqw = buildQueryWrapper(bo);
|
||||
return categoryMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询流程分类树结构信息
|
||||
*
|
||||
* @param category 流程分类信息
|
||||
* @return 流程分类树信息集合
|
||||
*/
|
||||
@Override
|
||||
public List<Tree<String>> selectCategoryTreeList(FlowCategoryBo category) {
|
||||
List<FlowCategoryVo> categoryList = this.queryList(category);
|
||||
if (CollUtil.isEmpty(categoryList)) {
|
||||
return CollUtil.newArrayList();
|
||||
}
|
||||
return TreeBuildUtils.buildMultiRoot(
|
||||
categoryList,
|
||||
node -> Convert.toStr(node.getCategoryId()),
|
||||
node -> Convert.toStr(node.getParentId()),
|
||||
(node, treeNode) -> treeNode
|
||||
.setId(Convert.toStr(node.getCategoryId()))
|
||||
.setParentId(Convert.toStr(node.getParentId()))
|
||||
.setName(node.getCategoryName())
|
||||
.setWeight(node.getOrderNum())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 工作流查询分类
|
||||
*
|
||||
* @return 分类树结构列表
|
||||
*/
|
||||
@Override
|
||||
public List<org.dromara.warm.flow.core.dto.Tree> queryCategory() {
|
||||
List<FlowCategoryVo> list = this.queryList(new FlowCategoryBo());
|
||||
return StreamUtils.toList(list, category -> new org.dromara.warm.flow.core.dto.Tree()
|
||||
.setId(Convert.toStr(category.getCategoryId()))
|
||||
.setName(category.getCategoryName())
|
||||
.setParentId(Convert.toStr(category.getParentId()))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验流程分类名称是否唯一
|
||||
*
|
||||
* @param category 流程分类信息
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public boolean checkCategoryNameUnique(FlowCategoryBo category) {
|
||||
boolean exist = categoryMapper.lambda()
|
||||
.eq(FlowCategory::getCategoryName, category.getCategoryName())
|
||||
.eq(FlowCategory::getParentId, category.getParentId())
|
||||
.neIfPresent(FlowCategory::getCategoryId, category.getCategoryId())
|
||||
.exists();
|
||||
return !exist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询流程分类是否存在流程定义
|
||||
*
|
||||
* @param categoryId 流程分类ID
|
||||
* @return 结果 true 存在 false 不存在
|
||||
*/
|
||||
@Override
|
||||
public boolean checkCategoryExistDefinition(Long categoryId) {
|
||||
FlowDefinition definition = new FlowDefinition();
|
||||
definition.setCategory(categoryId.toString());
|
||||
return defService.exists(definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在流程分类子节点
|
||||
*
|
||||
* @param categoryId 流程分类ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public boolean hasChildByCategoryId(Long categoryId) {
|
||||
return categoryMapper.lambda().eq(FlowCategory::getParentId, categoryId).exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建流程分类查询条件。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 查询条件包装器
|
||||
*/
|
||||
private LambdaQueryWrapper<FlowCategory> buildQueryWrapper(FlowCategoryBo bo) {
|
||||
LambdaQueryWrapper<FlowCategory> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(FlowCategory::getDelFlag, SystemConstants.NORMAL);
|
||||
lqw.eq(ObjectUtil.isNotNull(bo.getCategoryId()), FlowCategory::getCategoryId, bo.getCategoryId());
|
||||
lqw.eq(ObjectUtil.isNotNull(bo.getParentId()), FlowCategory::getParentId, bo.getParentId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getCategoryName()), FlowCategory::getCategoryName, bo.getCategoryName());
|
||||
lqw.orderByAsc(FlowCategory::getAncestors);
|
||||
lqw.orderByAsc(FlowCategory::getParentId);
|
||||
lqw.orderByAsc(FlowCategory::getOrderNum);
|
||||
lqw.orderByAsc(FlowCategory::getCategoryId);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增流程分类
|
||||
*
|
||||
* @param bo 流程分类
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
@Override
|
||||
public int insertByBo(FlowCategoryBo bo) {
|
||||
FlowCategory info = categoryMapper.selectById(bo.getParentId());
|
||||
if (ObjectUtil.isNull(info)) {
|
||||
throw new ServiceException("父级流程分类不存在!");
|
||||
}
|
||||
FlowCategory category = MapstructUtils.convert(bo, FlowCategory.class);
|
||||
category.setAncestors(info.getAncestors() + StringUtils.SEPARATOR + category.getParentId());
|
||||
return categoryMapper.insert(category);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改流程分类
|
||||
*
|
||||
* @param bo 流程分类
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@CacheEvict(cacheNames = FlowConstant.FLOW_CATEGORY_NAME, key = "#bo.categoryId")
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int updateByBo(FlowCategoryBo bo) {
|
||||
FlowCategory category = MapstructUtils.convert(bo, FlowCategory.class);
|
||||
FlowCategory oldCategory = categoryMapper.selectById(category.getCategoryId());
|
||||
if (ObjectUtil.isNull(oldCategory)) {
|
||||
throw new ServiceException("流程分类不存在,无法修改");
|
||||
}
|
||||
if (oldCategory.getParentId() == 0L && category.getParentId() != 0L) {
|
||||
throw new ServiceException("不允许修改顶级分类的父级节点");
|
||||
}
|
||||
if (!oldCategory.getParentId().equals(category.getParentId())) {
|
||||
FlowCategory newParentCategory = categoryMapper.selectById(category.getParentId());
|
||||
if (ObjectUtil.isNotNull(newParentCategory)) {
|
||||
String newAncestors = newParentCategory.getAncestors() + StringUtils.SEPARATOR + newParentCategory.getCategoryId();
|
||||
String oldAncestors = oldCategory.getAncestors();
|
||||
category.setAncestors(newAncestors);
|
||||
updateCategoryChildren(category.getCategoryId(), newAncestors, oldAncestors);
|
||||
} else {
|
||||
throw new ServiceException("父级流程分类不存在!");
|
||||
}
|
||||
} else {
|
||||
category.setAncestors(oldCategory.getAncestors());
|
||||
}
|
||||
return categoryMapper.updateById(category);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改子元素关系
|
||||
*
|
||||
* @param categoryId 被修改的流程分类ID
|
||||
* @param newAncestors 新的父ID集合
|
||||
* @param oldAncestors 旧的父ID集合
|
||||
*/
|
||||
private void updateCategoryChildren(Long categoryId, String newAncestors, String oldAncestors) {
|
||||
List<FlowCategory> children = categoryMapper.lambda()
|
||||
.findInSet(categoryId, FlowCategory::getAncestors)
|
||||
.list();
|
||||
List<FlowCategory> list = new ArrayList<>();
|
||||
for (FlowCategory child : children) {
|
||||
FlowCategory category = new FlowCategory();
|
||||
category.setCategoryId(child.getCategoryId());
|
||||
category.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
|
||||
list.add(category);
|
||||
}
|
||||
if (CollUtil.isNotEmpty(list)) {
|
||||
categoryMapper.updateBatchById(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除流程分类信息
|
||||
*
|
||||
* @param categoryId 主键
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@CacheEvict(cacheNames = FlowConstant.FLOW_CATEGORY_NAME, key = "#categoryId")
|
||||
@Override
|
||||
public int deleteWithValidById(Long categoryId) {
|
||||
return categoryMapper.deleteById(categoryId);
|
||||
}
|
||||
}
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
package org.dromara.workflow.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.service.DictService;
|
||||
import org.dromara.common.core.utils.DateUtils;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.system.api.DeptService;
|
||||
import org.dromara.system.api.UserService;
|
||||
import org.dromara.system.api.domain.UserDTO;
|
||||
import org.dromara.warm.flow.core.dto.DefJson;
|
||||
import org.dromara.warm.flow.core.dto.NodeJson;
|
||||
import org.dromara.warm.flow.core.dto.PromptContent;
|
||||
import org.dromara.warm.flow.core.enums.NodeType;
|
||||
import org.dromara.warm.flow.core.utils.MapUtil;
|
||||
import org.dromara.warm.flow.orm.entity.FlowHisTask;
|
||||
import org.dromara.warm.flow.orm.mapper.FlowHisTaskMapper;
|
||||
import org.dromara.warm.flow.ui.service.ChartExtService;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.common.constant.FlowConstant;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 流程图提示信息
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class FlwChartExtServiceImpl implements ChartExtService {
|
||||
|
||||
private final UserService userService;
|
||||
private final DeptService deptService;
|
||||
private final FlowHisTaskMapper flowHisTaskMapper;
|
||||
private final DictService dictService;
|
||||
@Value("${warm-flow.node-tooltip:true}")
|
||||
private boolean nodeTooltip;
|
||||
|
||||
/**
|
||||
* 设置流程图提示信息
|
||||
*
|
||||
* @param defJson 流程定义json对象
|
||||
*/
|
||||
@Override
|
||||
public void execute(DefJson defJson) {
|
||||
// 配置关闭,直接返回,不渲染悬浮窗
|
||||
if (!nodeTooltip) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据流程实例ID查询所有相关的历史任务列表
|
||||
List<FlowHisTask> flowHisTasks = this.getHisTaskGroupedByNode(defJson.getInstance().getId());
|
||||
if (CollUtil.isEmpty(flowHisTasks)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 按节点编号(nodeCode)对历史任务进行分组
|
||||
Map<String, List<FlowHisTask>> groupedByNode = StreamUtils.groupByKey(flowHisTasks, FlowHisTask::getNodeCode);
|
||||
|
||||
// 批量查询所有审批人的用户信息
|
||||
List<UserDTO> userDTOList = userService.selectListByIds(StreamUtils.toSet(flowHisTasks, e -> Convert.toLong(e.getApprover())));
|
||||
|
||||
// 将查询到的用户列表转换为以用户ID为key的映射
|
||||
Map<Long, UserDTO> userMap = StreamUtils.toIdentityMap(userDTOList, UserDTO::getUserId);
|
||||
Set<Long> deptIds = StreamUtils.toSet(userDTOList, UserDTO::getDeptId);
|
||||
Map<Long, String> deptNameMap = deptService.selectDeptNamesByIds(deptIds);
|
||||
|
||||
Map<String, String> dictType = dictService.getAllDictByDictType(FlowConstant.WF_TASK_STATUS);
|
||||
|
||||
for (NodeJson nodeJson : defJson.getNodeList()) {
|
||||
List<FlowHisTask> taskList = groupedByNode.get(nodeJson.getNodeCode());
|
||||
if (CollUtil.isEmpty(taskList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 按审批人分组去重,保留最新处理记录,最终转换成 List
|
||||
List<FlowHisTask> latestPerApprover = taskList.stream()
|
||||
.collect(Collectors.collectingAndThen(
|
||||
Collectors.toMap(
|
||||
FlowHisTask::getApprover,
|
||||
Function.identity(),
|
||||
(oldTask, newTask) -> newTask.getUpdateTime().after(oldTask.getUpdateTime()) ? newTask : oldTask,
|
||||
LinkedHashMap::new
|
||||
),
|
||||
map -> new ArrayList<>(map.values())
|
||||
));
|
||||
|
||||
// 处理当前节点的扩展信息
|
||||
this.processNodeExtInfo(nodeJson, latestPerApprover, userMap, deptNameMap, dictType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化流程图提示信息
|
||||
*
|
||||
* @param defJson 流程定义json对象
|
||||
*/
|
||||
@Override
|
||||
public void initPromptContent(DefJson defJson) {
|
||||
// 配置关闭,直接返回,不渲染悬浮窗
|
||||
if (!nodeTooltip) {
|
||||
return;
|
||||
}
|
||||
|
||||
defJson.setTopText("流程名称: " + defJson.getFlowName());
|
||||
defJson.getNodeList().forEach(nodeJson -> {
|
||||
nodeJson.setPromptContent(
|
||||
new PromptContent()
|
||||
// 提示信息
|
||||
.setInfo(
|
||||
CollUtil.newArrayList(
|
||||
new PromptContent.InfoItem()
|
||||
.setPrefix("任务名称: ")
|
||||
.setContent(nodeJson.getNodeName())
|
||||
.setContentStyle(Map.of(
|
||||
"border", "1px solid #d1e9ff",
|
||||
"backgroundColor", "#e8f4ff",
|
||||
"padding", "4px 8px",
|
||||
"borderRadius", "4px"
|
||||
))
|
||||
.setRowStyle(Map.of(
|
||||
"fontWeight", "bold",
|
||||
"margin", "0 0 6px 0",
|
||||
"padding", "0 0 8px 0",
|
||||
"borderBottom", "1px solid #ccc"
|
||||
))
|
||||
)
|
||||
)
|
||||
// 弹窗样式
|
||||
.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",
|
||||
"maxHeight", "300px",
|
||||
"overflowY", "auto",
|
||||
"overflowX", "hidden",
|
||||
"color", "#333",
|
||||
"pointerEvents", "auto",
|
||||
"scrollbarWidth", "thin"
|
||||
))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理节点的扩展信息,构建用于流程图悬浮提示的内容
|
||||
*
|
||||
* @param nodeJson 当前流程节点对象,包含节点基础信息和提示内容容器
|
||||
* @param taskList 当前节点关联的历史审批任务列表,用于生成提示信息
|
||||
* @param userMap 用户信息映射表,key 为用户ID,value 为用户DTO对象,用于获取审批人信息
|
||||
* @param deptNameMap 部门名称映射表,key 为部门ID,value 为部门名称
|
||||
* @param dictType 数据字典映射表,key 为字典项编码,value 为对应显示值,用于翻译审批状态等
|
||||
*/
|
||||
private void processNodeExtInfo(NodeJson nodeJson, List<FlowHisTask> taskList, Map<Long, UserDTO> userMap,
|
||||
Map<Long, String> deptNameMap, Map<String, String> dictType) {
|
||||
|
||||
// 获取节点提示内容对象中的 info 列表,用于追加提示项
|
||||
List<PromptContent.InfoItem> info = nodeJson.getPromptContent().getInfo();
|
||||
|
||||
// 遍历所有任务记录,构建提示内容
|
||||
for (FlowHisTask task : taskList) {
|
||||
UserDTO userDTO = userMap.get(Convert.toLong(task.getApprover()));
|
||||
if (ObjectUtil.isEmpty(userDTO)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String deptName = deptNameMap.getOrDefault(userDTO.getDeptId(), StringUtils.EMPTY);
|
||||
|
||||
// 添加标题项,如:👤 张三(市场部)
|
||||
info.add(new PromptContent.InfoItem()
|
||||
.setPrefix(StringUtils.format("👥 {}({})", userDTO.getNickName(), deptName))
|
||||
.setPrefixStyle(Map.of(
|
||||
"fontWeight", "bold",
|
||||
"fontSize", "15px",
|
||||
"color", "#333"
|
||||
))
|
||||
.setRowStyle(Map.of(
|
||||
"margin", "8px 0",
|
||||
"borderBottom", "1px dashed #ccc"
|
||||
))
|
||||
);
|
||||
|
||||
// 添加具体信息项:账号、耗时、时间
|
||||
info.add(buildInfoItem("用户账号", userDTO.getUserName()));
|
||||
info.add(buildInfoItem("审批状态", dictType.get(task.getFlowStatus())));
|
||||
info.add(buildInfoItem("审批耗时", DateUtils.formatBetweenBySecond(task.getUpdateTime(), task.getCreateTime())));
|
||||
info.add(buildInfoItem("办理时间", DateUtils.formatDateTime(task.getUpdateTime())));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建单条提示内容对象 InfoItem,用于悬浮窗显示(key: value)
|
||||
*
|
||||
* @param key 字段名(作为前缀)
|
||||
* @param value 字段值
|
||||
* @return 提示项对象
|
||||
*/
|
||||
private PromptContent.InfoItem buildInfoItem(String key, String value) {
|
||||
return new PromptContent.InfoItem()
|
||||
// 前缀
|
||||
.setPrefix(key + ": ")
|
||||
// 前缀样式
|
||||
.setPrefixStyle(Map.of(
|
||||
"textAlign", "right",
|
||||
"color", "#444",
|
||||
"userSelect", "none",
|
||||
"display", "inline-block",
|
||||
"width", "100px",
|
||||
"paddingRight", "8px",
|
||||
"fontWeight", "500",
|
||||
"fontSize", "14px",
|
||||
"lineHeight", "24px",
|
||||
"verticalAlign", "middle"
|
||||
))
|
||||
// 内容
|
||||
.setContent(value)
|
||||
// 内容样式
|
||||
.setContentStyle(Map.of(
|
||||
"backgroundColor", "#f7faff",
|
||||
"color", "#005cbf",
|
||||
"padding", "4px 8px",
|
||||
"fontSize", "14px",
|
||||
"borderRadius", "4px",
|
||||
"whiteSpace", "normal",
|
||||
"border", "1px solid #d0e5ff",
|
||||
"userSelect", "text",
|
||||
"lineHeight", "20px"
|
||||
))
|
||||
// 行样式
|
||||
.setRowStyle(Map.of(
|
||||
"color", "#222",
|
||||
"alignItems", "center",
|
||||
"display", "flex",
|
||||
"marginBottom", "6px",
|
||||
"fontWeight", "400",
|
||||
"fontSize", "14px"
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据流程实例ID获取历史任务列表
|
||||
*
|
||||
* @param instanceId 流程实例ID
|
||||
* @return 按更新时间倒序排列的审批历史任务列表
|
||||
*/
|
||||
public List<FlowHisTask> getHisTaskGroupedByNode(Long instanceId) {
|
||||
LambdaQueryWrapper<FlowHisTask> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(FlowHisTask::getInstanceId, instanceId)
|
||||
.eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey())
|
||||
.orderByDesc(FlowHisTask::getUpdateTime);
|
||||
return flowHisTaskMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user