流转工作流程

This commit is contained in:
2024-05-12 16:15:34 +08:00
parent 1a9beeed93
commit eb9818dd7f
71 changed files with 3810 additions and 302 deletions

View File

@@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RequestParam;
*
* @author 芋道源码
*/
@FeignClient(value = ServerInfo.SUPERVISION,path = "/process",fallbackFactory = BpmProcessFeignClientFallbackFactory.class)
@FeignClient(value = ServerInfo.BPM,path = "/bpm/processDefinition",fallbackFactory = BpmProcessFeignClientFallbackFactory.class)
public interface BpmProcessFeignClient {
/**

View File

@@ -0,0 +1,46 @@
package com.njcn.bpm.enums;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程任务的 Comment 评论类型枚举
*
* @author kehaiyou
*/
@Getter
@AllArgsConstructor
public enum BpmCommentTypeEnum {
APPROVE("1", "审批通过", "审批通过,原因是:{}"),
REJECT("2", "不通过", "审批不通过:原因是:{}"),
CANCEL("3", "已取消", "系统自动取消,原因是:{}"),
RETURN("4", "退回", "任务被退回,原因是:{}"),
DELEGATE_START("5", "委派发起", "[{}]将任务委派给[{}],委派理由为:{}"),
DELEGATE_END("6", "委派完成", "[{}]完成委派任务,任务重新回到[{}]手中,审批建议为:{}"),
TRANSFER("7", "转派", "[{}]将任务转派给[{}],转派理由为:{}"),
ADD_SIGN("8", "加签", "[{}]{}给了[{}],理由为:{}"),
SUB_SIGN("9", "减签", "[{}]操作了【减签】,审批人[{}]的任务被取消"),
;
/**
* 操作类型
*
* 由于 BPM Comment 类型为 String所以这里就不使用 Integer
*/
private final String type;
/**
* 操作名字
*/
private final String name;
/**
* 操作描述
*/
private final String comment;
public String formatComment(Object... params) {
return StrUtil.format(comment, params);
}
}

View File

@@ -0,0 +1,45 @@
package com.njcn.bpm.enums;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程实例/任务的删除原因枚举
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum BpmDeleteReasonEnum {
// ========== 流程实例的独有原因 ==========
REJECT_TASK("审批不通过任务,原因:{}"), // 场景:用户审批不通过任务。修改文案时,需要注意 isRejectReason 方法
CANCEL_PROCESS_INSTANCE_BY_START_USER("用户主动取消流程,原因:{}"), // 场景:用户主动取消流程
CANCEL_PROCESS_INSTANCE_BY_ADMIN("管理员【{}】取消流程,原因:{}"), // 场景:管理员取消流程
// ========== 流程任务的独有原因 ==========
CANCEL_BY_SYSTEM("系统自动取消"), // 场景非常多比如说1多任务审批已经满足条件无需审批该任务2流程实例被取消无需审批该任务等等
;
private final String reason;
/**
* 格式化理由
*
* @param args 参数
* @return 理由
*/
public String format(Object... args) {
return StrUtil.format(reason, args);
}
// ========== 逻辑 ==========
public static boolean isRejectReason(String reason) {
return StrUtil.startWith(reason, "审批不通过任务,原因:");
}
}

View File

@@ -4,6 +4,7 @@ import lombok.Getter;
/**
* 异常处理类
*
* @author qijian
* @version 1.0.0
* @date 2022年11月11日 09:56
@@ -15,33 +16,58 @@ public enum BpmResponseEnum {
* 过程监督异常响应码的范围:
* A00550 ~ A00649
*/
BPM_COMMON_ERROR("A00568","工作流模块异常"),
BPM_COMMON_ERROR("A00568", "工作流模块异常"),
BPM_XML_ERROR("A00568","流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!"),
BPM_XML_ERROR("A00568", "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!"),
BPM_MODEL_REPEAT("A00568","流程标识已存在"),
BPM_MODEL_REPEAT("A00568", "流程标识已存在"),
BPM_MODEL_NOT_EXIST("A00568","流程模型不存在"),
BPM_MODEL_NOT_EXIST("A00568", "流程模型不存在"),
PROCESS_DEFINITION_NOT_EXISTS("A00568","流程定义不存在"),
PROCESS_DEFINITION_NOT_EXISTS("A00568", "流程定义不存在"),
TASK_NOT_EXISTS("A00568", "流程任务不存在"),
TASK_OPERATE_FAIL_ASSIGN_NOT_SELF("A00568", "操作失败,原因:该任务的审批人不是你"),
PROCESS_DEFINITION_IS_SUSPENDED("A00568", "流程定义处于挂起状态"),
FORM_NOT_EXISTS("A00568","动态表单不存在"),
FORM_NOT_EXISTS("A00568", "动态表单不存在"),
MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG("A00568","部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置"),
MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG("A00568", "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置"),
BPM_START_EVENT_NOT_EXIST("A00568","起始事件不存在"),
BPM_START_EVENT_NOT_EXIST("A00568", "起始事件不存在"),
MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS("A00568","部署流程失败原因BPMN 流程图中,用户任务的名字不存在"),
MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS("A00568", "部署流程失败原因BPMN 流程图中,用户任务的名字不存在"),
REPEAT_NAME_FORM("A00568","流程表单名称重复"),
REPEAT_NAME_FORM("A00568", "流程表单名称重复"),
REPEAT_CATEGORY_NAME_FORM("A00568","流程类型名称重复"),
REPEAT_CATEGORY_NAME_FORM("A00568", "流程类型名称重复"),
PROCESS_INSTANCE_NOT_EXISTS ("A00568", "流程实例不存在"),
TASK_IS_PENDING ("A00568", "当前任务处于挂起状态,不能操作"),
TASK_TARGET_NODE_NOT_EXISTS ("A00568", " 目标节点不存在"),
TASK_RETURN_FAIL_SOURCE_TARGET_ERROR ("A00568", "回退任务失败,目标节点是在并行网关上或非同一路线上,不可跳转"),
PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS("A00568", "流程取消失败,流程不处于运行中"),
TASK_DELEGATE_FAIL_USER_REPEAT("A00568", "任务委派失败,委派人和当前审批人为同一人"),
TASK_DELEGATE_FAIL_USER_NOT_EXISTS("A00568", "任务委派失败,被委派人不存在"),
TASK_TRANSFER_FAIL_USER_REPEAT("A00568", "任务转办失败,转办人和当前审批人为同一人"),
TASK_TRANSFER_FAIL_USER_NOT_EXISTS("A00568", "任务转办失败,转办人不存在"),
TASK_SIGN_CREATE_USER_NOT_EXIST("A00568", "任务加签:选择的用户不存在"),
TASK_SIGN_DELETE_NO_PARENT("A00568", "任务减签失败,被减签的任务必须是通过加签生成的任务"),
PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF("A00568", "流程取消失败,该流程不是你发起的");
;
private final String code;

View File

@@ -0,0 +1,47 @@
package com.njcn.bpm.enums;
import cn.hutool.core.util.ArrayUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程任务的加签类型枚举
*
* @author kehaiyou
*/
@Getter
@AllArgsConstructor
public enum BpmTaskSignTypeEnum {
/**
* 向前加签,需要前置任务审批完成,才回到原审批人
*/
BEFORE("before", "向前加签"),
/**
* 向后加签,需要后置任务全部审批完,才会通过原审批人节点
*/
AFTER("after", "向后加签");
/**
* 类型
*/
private final String type;
/**
* 名字
*/
private final String name;
public static String nameOfType(String type) {
for (BpmTaskSignTypeEnum value : values()) {
if (value.type.equals(type)) {
return value.name;
}
}
return null;
}
public static BpmTaskSignTypeEnum of(String type) {
return ArrayUtil.firstMatch(value -> value.getType().equals(type), values());
}
}

View File

@@ -0,0 +1,61 @@
package com.njcn.bpm.enums;
import com.njcn.bpm.utils.ObjectUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程任务 Task 的状态枚举
*
* @author jason
*/
@Getter
@AllArgsConstructor
public enum BpmTaskStatusEnum {
RUNNING(1, "审批中"),
APPROVE(2, "审批通过"),
REJECT(3, "审批不通过"),
CANCEL(4, "已取消"),
RETURN(5, "已退回"),
DELEGATE(6, "委派中"),
/**
* 使用场景:
* 1. 任务被向后【加签】时,它在审批通过后,会变成 APPROVING 这个状态,然后等到【加签】出来的任务都被审批后,才会变成 APPROVE 审批通过
*/
APPROVING(7, "审批通过中"),
/**
* 使用场景:
* 1. 任务被向前【加签】时,它会变成 WAIT 状态,需要等待【加签】出来的任务被审批后,它才能继续变为 RUNNING 继续审批
* 2. 任务被向后【加签】时,【加签】出来的任务处于 WAIT 状态,它们需要等待该任务被审批后,它们才能继续变为 RUNNING 继续审批
*/
WAIT(0, "待审批");
/**
* 状态
* <p>
* 如果新增时,注意 {@link #isEndStatus(Integer)} 是否需要变更
*/
private final Integer status;
/**
* 名字
*/
private final String name;
/**
* 判断该状态是否已经处于 End 最终状态
* <p>
* 主要用于一些状态更新的逻辑,如果已经是最终状态,就不再进行更新
*
* @param status 状态
* @return 是否
*/
public static boolean isEndStatus(Integer status) {
return ObjectUtils.equalsAny(status,
APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus(),
RETURN.getStatus(), APPROVING.getStatus());
}
}

View File

@@ -0,0 +1,34 @@
package com.njcn.bpm.enums;
/**
* Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
*
* 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 enums 包下
*
* @author 芋道源码
*/
public interface WebFilterOrderEnum {
int CORS_FILTER = Integer.MIN_VALUE;
int TRACE_FILTER = CORS_FILTER + 1;
int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500;
// OrderedRequestContextFilter 默认为 -105用于国际化上下文等等
int TENANT_CONTEXT_FILTER = - 104; // 需要保证在 ApiAccessLogFilter 前面
int API_ACCESS_LOG_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面
int XSS_FILTER = -102; // 需要保证在 RequestBodyCacheFilter 后面
// Spring Security Filter 默认为 -100可见 org.springframework.boot.autoconfigure.security.SecurityProperties 配置属性类
int TENANT_SECURITY_FILTER = -99; // 需要保证在 Spring Security 过滤器后面
int FLOWABLE_FILTER = -98; // 需要保证在 Spring Security 过滤后面
int DEMO_FILTER = Integer.MAX_VALUE;
}

View File

@@ -0,0 +1,27 @@
package com.njcn.bpm.pojo.dto;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* BPM 发送流程实例被通过 Request DTO
*/
@Data
public class BpmMessageSendWhenProcessInstanceApproveReqDTO {
/**
* 流程实例的编号
*/
@NotEmpty(message = "流程实例的编号不能为空")
private String processInstanceId;
/**
* 流程实例的名字
*/
@NotEmpty(message = "流程实例的名字不能为空")
private String processInstanceName;
@NotNull(message = "发起人的用户编号")
private String startUserId;
}

View File

@@ -0,0 +1,34 @@
package com.njcn.bpm.pojo.dto;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* BPM 发送流程实例被不通过 Request DTO
*/
@Data
public class BpmMessageSendWhenProcessInstanceRejectReqDTO {
/**
* 流程实例的编号
*/
@NotEmpty(message = "流程实例的编号不能为空")
private String processInstanceId;
/**
* 流程实例的名字
*/
@NotEmpty(message = "流程实例的名字不能为空")
private String processInstanceName;
@NotNull(message = "发起人的用户编号")
private String startUserId;
/**
* 不通过理由
*/
@NotEmpty(message = "不通过理由不能为空")
private String reason;
}

View File

@@ -24,14 +24,14 @@ public class BpmCategoryParam implements Serializable {
* 分类名
*/
@ApiModelProperty("分类名")
@NotNull(message = "分类名不能为空")
@NotBlank(message = "分类名不能为空")
private String name;
/**
* 分类标志
*/
@ApiModelProperty("分类标志")
@NotNull(message = "分类标志不能为空")
@NotBlank(message = "分类标志不能为空")
private String code;
/**

View File

@@ -20,21 +20,21 @@ public class BpmFormParam implements Serializable {
* 表单名
*/
@ApiModelProperty("表单名称")
@NotNull(message = "表单名称不能为空")
@NotBlank(message = "表单名称不能为空")
private String name;
/**
* 状态
*/
@ApiModelProperty("表单状态")
@NotNull(message = "表单状态不能为空")
@NotBlank(message = "表单状态不能为空")
private Integer status;
/**
* 表单的配置
*/
@ApiModelProperty("表单的配置-JSON 字符串")
@NotNull(message = "表单的配置不能为空")
@NotBlank(message = "表单的配置不能为空")
private String conf;
/**
@@ -42,7 +42,7 @@ public class BpmFormParam implements Serializable {
*
*/
@ApiModelProperty("表单项的数组-JSON 字符串的数组")
@NotNull(message = "表单项的数组不能为空")
@NotBlank(message = "表单项的数组不能为空")
private List<String> fields;
/**

View File

@@ -20,11 +20,11 @@ public class BpmModelParam implements Serializable {
@ApiModelProperty("流程标识")
@NotNull(message = "流程标识不能为空")
@NotBlank(message = "流程标识不能为空")
private String key;
@ApiModelProperty("流程名称")
@NotNull(message = "流程名称不能为空")
@NotBlank(message = "流程名称不能为空")
private String name;
@ApiModelProperty( "流程图标")

View File

@@ -0,0 +1,21 @@
package com.njcn.bpm.pojo.param.instance;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Schema(description = "管理后台 - 流程实例的取消 Request VO")
@Data
public class BpmProcessInstanceCancelParam {
@ApiModelProperty("流程实例的编号")
@NotEmpty(message = "流程实例的编号不能为空")
private String id;
@ApiModelProperty("取消原因")
@NotEmpty(message = "取消原因不能为空")
private String reason;
}

View File

@@ -0,0 +1,26 @@
package com.njcn.bpm.pojo.param.instance;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import java.util.List;
import java.util.Map;
@Schema(description = "管理后台 - 流程实例的创建 Request VO")
@Data
public class BpmProcessInstanceCreateParam {
@NotEmpty(message = "流程定义编号不能为空")
@ApiModelProperty("流程定义的编号")
private String processDefinitionId;
@Schema(description = "变量实例(动态表单)")
@ApiModelProperty("变量实例(动态表单)")
private Map<String, Object> variables;
@ApiModelProperty("发起人自选审批人 Map")
private Map<String, List<Long>> startUserSelectAssignees;
}

View File

@@ -0,0 +1,28 @@
package com.njcn.bpm.pojo.param.instance;
import com.njcn.web.pojo.param.BaseParam;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 流程实例分页")
@Data
public class BpmProcessInstancePageParam extends BaseParam {
@ApiModelProperty("流程名称")
private String name;
@ApiModelProperty("流程定义的编号")
private String processDefinitionId;
@ApiModelProperty("流程实例的状态")
private Integer status;
@ApiModelProperty("流程分类")
private String category;
@ApiModelProperty("发起用户编号")
private String startUserId; // 注意,只有在【流程实例】菜单,才使用该参数
}

View File

@@ -0,0 +1,29 @@
package com.njcn.bpm.pojo.param.task;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.util.Collection;
import java.util.Map;
@Schema(description = "管理后台 - 通过流程任务的 Request VO")
@Data
public class BpmTaskApproveParam {
@ApiModelProperty("任务编号")
@NotBlank(message = "任务编号不能为空")
private String id;
@ApiModelProperty("审批意见")
@NotBlank(message = "审批意见不能为空")
private String reason;
@ApiModelProperty("抄送的用户编号数组")
private Collection<String> copyUserIds;
@ApiModelProperty("变量实例(动态表单)")
private Map<String, Object> variables;
}

View File

@@ -0,0 +1,26 @@
package com.njcn.bpm.pojo.param.task;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 委派流程任务的 Request VO")
@Data
public class BpmTaskDelegateParam {
@ApiModelProperty("任务编号")
@NotEmpty(message = "任务编号不能为空")
private String id;
@ApiModelProperty("被委派人ID")
@NotNull(message = "被委派人 ID 不能为空")
private String delegateUserId;
@ApiModelProperty("委派原因")
@NotEmpty(message = "委派原因不能为空")
private String reason;
}

View File

@@ -0,0 +1,35 @@
package com.njcn.bpm.pojo.param.task;
import com.njcn.bpm.pojo.param.PageParam;
import com.njcn.web.pojo.param.BaseParam;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - 流程任务的的分页 Request VO") // 待办、已办,都使用该分页
@Data
public class BpmTaskParam extends PageParam {
@Schema(description = "流程任务名", example = "芋道")
private String name;
/**
* 待办事项分页查询实体
*/
@Data
@EqualsAndHashCode(callSuper = true)
public static class BpmTaskQueryParam extends BaseParam {
/**
* 表单名称
*/
@ApiModelProperty("流程任务名")
private String name;
}
}

View File

@@ -0,0 +1,24 @@
package com.njcn.bpm.pojo.param.task;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
@Schema(description = "管理后台 - 不通过流程任务的 Request VO")
@Data
public class BpmTaskRejectParam {
@ApiModelProperty("任务编号")
@NotBlank(message = "任务编号不能为空")
private String id;
@ApiModelProperty("审批意见")
@NotBlank(message = "审批意见不能为空")
private String reason;
}

View File

@@ -0,0 +1,29 @@
package com.njcn.bpm.pojo.param.task;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
@Schema(description = "管理后台 - 回退流程任务的 Request VO")
@Data
public class BpmTaskReturnParam {
@ApiModelProperty("任务编号")
@NotBlank(message = "任务编号不能为空")
private String id;
@ApiModelProperty("回退到的任务 Key")
@NotBlank(message = "回退到的任务 Key 不能为空")
private String targetTaskDefinitionKey;
@ApiModelProperty("回退意见")
@NotBlank(message = "回退意见不能为空")
private String reason;
}

View File

@@ -0,0 +1,31 @@
package com.njcn.bpm.pojo.param.task;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import java.util.List;
import java.util.Set;
@Schema(description = "管理后台 - 加签任务的创建(加签) Request VO")
@Data
public class BpmTaskSignCreateParam {
@ApiModelProperty("需要加签的任务编号")
@NotEmpty(message = "任务编号不能为空")
private String id;
@ApiModelProperty("加签的用户编号")
@NotEmpty(message = "加签用户不能为空")
private List<String> userIds;
@ApiModelProperty("加签类型")
@NotEmpty(message = "加签类型不能为空")
private String type; // 参见 BpmTaskSignTypeEnum 枚举
@ApiModelProperty("加签原因")
@NotEmpty(message = "加签原因不能为空")
private String reason;
}

View File

@@ -0,0 +1,21 @@
package com.njcn.bpm.pojo.param.task;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Schema(description = "管理后台 - 加签任务的删除(减签) Request VO")
@Data
public class BpmTaskSignDeleteParam {
@ApiModelProperty("被减签的任务编号")
@NotEmpty(message = "任务编号不能为空")
private String id;
@ApiModelProperty("减签原因")
@NotEmpty(message = "减签原因不能为空")
private String reason;
}

View File

@@ -0,0 +1,26 @@
package com.njcn.bpm.pojo.param.task;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 流程任务的转办 Request VO")
@Data
public class BpmTaskTransferParam {
@ApiModelProperty("任务编号")
@NotEmpty(message = "任务编号不能为空")
private String id;
@ApiModelProperty("新审批人的用户编号")
@NotNull(message = "新审批人的用户编号不能为空")
private String assigneeUserId;
@ApiModelProperty("转办原因")
@NotEmpty(message = "转办原因不能为空")
private String reason;
}

View File

@@ -15,7 +15,7 @@ import java.util.List;
/**
* BPM 流程定义的拓信息
* 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表
* 主要解决 Flowable {@link} 不支持拓展字段,所以新建该表
*
*/
@TableName(value = "bpm_process_definition_info", autoResultMap = true)

View File

@@ -0,0 +1,70 @@
package com.njcn.bpm.pojo.po.task;
import com.baomidou.mybatisplus.annotation.TableName;
import com.njcn.db.bo.BaseEntity;
import java.io.Serializable;
import lombok.Data;
/**
* <p>
* BPM 流程实例抄送表
* </p>
*
* @author hongawen
* @since 2024-05-10
*/
@Data
@TableName("bpm_process_instance_copy")
public class ProcessInstanceCopy extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 编号
*/
private String id;
/**
* 用户编号,被抄送人
*/
private String userId;
/**
* 发起流程的用户编号
*/
private String startUserId;
/**
* 流程实例的id
*/
private String processInstanceId;
/**
* 流程实例的名字
*/
private String processInstanceName;
/**
* 流程定义的分类
*/
private String category;
/**
* 发起抄送的任务编号
*/
private String taskId;
/**
* 任务的名字
*/
private String taskName;
/**
* 状态0-删除 1-正常
*/
private Integer state;
}

View File

@@ -13,7 +13,6 @@ import java.util.List;
/**
* BPM 流程定义的拓信息
* 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表
*
*/
@Data

View File

@@ -0,0 +1,93 @@
package com.njcn.bpm.pojo.vo.instance;
import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@Schema(description = "管理后台 - 流程实例的VO")
@Data
public class BpmProcessInstanceVO {
@ApiModelProperty("流程实例的编号")
private String id;
@ApiModelProperty("流程名称")
private String name;
@ApiModelProperty("流程分类")
private String category;
@ApiModelProperty("流程分类名称")
private String categoryName;
@ApiModelProperty("流程实例的状态")
private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举
@ApiModelProperty("发起时间")
private LocalDateTime startTime;
@ApiModelProperty("结束时间")
private LocalDateTime endTime;
@ApiModelProperty("持续时间")
private Long durationInMillis;
@ApiModelProperty("提交的表单值")
private Map<String, Object> formVariables;
@ApiModelProperty("业务的唯一标识")
private String businessKey;
/**
* 发起流程的用户
*/
private User startUser;
@ApiModelProperty("流程定义的编号")
private String processDefinitionId;
/**
* 流程定义
*/
private BpmProcessDefinitionInfoVO processDefinition;
/**
* 当前审批中的任务
*/
private List<Task> tasks; // 仅在流程实例分页才返回
@Schema(description = "用户信息")
@Data
public static class User {
@ApiModelProperty("用户编号")
private String id;
@ApiModelProperty("用户昵称")
private String name;
@ApiModelProperty("部门编号")
private String deptId;
@ApiModelProperty("部门名称")
private String deptName;
}
@Schema(description = "流程任务")
@Data
public static class Task {
@ApiModelProperty("流程任务的编号")
private String id;
@ApiModelProperty("任务名称")
private String name;
}
}

View File

@@ -0,0 +1,27 @@
package com.njcn.bpm.pojo.vo.task;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 流程活动的 Response VO")
@Data
public class BpmActivityVO {
@ApiModelProperty("流程活动的标识")
private String key;
@ApiModelProperty("流程活动的类型")
private String type;
@ApiModelProperty("流程活动的开始时间")
private LocalDateTime startTime;
@ApiModelProperty("流程活动的结束时间")
private LocalDateTime endTime;
@ApiModelProperty("关联的流程任务的编号")
private String taskId; // 关联的流程任务,只有 UserTask 等类型才有
}

View File

@@ -0,0 +1,106 @@
package com.njcn.bpm.pojo.vo.task;
import com.njcn.bpm.pojo.vo.instance.BpmProcessInstanceVO;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@Schema(description = "管理后台-流程任务VO")
@Data
public class BpmTaskVO {
@ApiModelProperty("任务编号")
private String id;
@ApiModelProperty("任务名字")
private String name;
@ApiModelProperty("创建时间")
private LocalDateTime createTime;
@ApiModelProperty("结束时间")
private LocalDateTime endTime;
@Schema(description = "持续时间", example = "1000")
@ApiModelProperty("持续时间")
private Long durationInMillis;
@ApiModelProperty("任务状态")
private Integer status; // 参见 BpmTaskStatusEnum 枚举
@ApiModelProperty("审批理由")
private String reason;
/**
* 负责人的用户信息
*/
private BpmProcessInstanceVO.User ownerUser;
/**
* 审核的用户信息
*/
private BpmProcessInstanceVO.User assigneeUser;
@ApiModelProperty("任务定义的标识")
private String taskDefinitionKey;
@ApiModelProperty("所属流程实例编号")
private String processInstanceId;
/**
* 所属流程实例
*/
private ProcessInstance processInstance;
@ApiModelProperty("父任务编号")
private String parentTaskId;
@ApiModelProperty("子任务列表(由加签生成)")
private List<BpmTaskVO> children;
@ApiModelProperty("表单编号")
private String formId;
@ApiModelProperty("表单名字")
private String formName;
@ApiModelProperty("表单的配置-JSON 字符串")
private String formConf;
@ApiModelProperty("表单项的数组")
private List<String> formFields;
@ApiModelProperty("提交的表单值")
private Map<String, Object> formVariables;
@Data
@Schema(description = "流程实例")
public static class ProcessInstance {
@ApiModelProperty("流程实例编号")
private String id;
@ApiModelProperty("流程实例名称")
private String name;
@ApiModelProperty("提交时间")
private LocalDateTime createTime;
@ApiModelProperty("流程定义的编号")
private String processDefinitionId;
/**
* 发起人的用户信息
*/
private BpmProcessInstanceVO.User startUser;
}
}

View File

@@ -2,6 +2,7 @@ package com.njcn.bpm.utils;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.bpm.pojo.dto.PageResult;
import java.util.List;
import java.util.function.Consumer;
@@ -42,20 +43,20 @@ public class BeanUtils {
}
return list;
}
//
// public static <S, T> Page<T> toBean(Page<S> source, Class<T> targetType) {
// return toBean(source, targetType, null);
// }
//
// public static <S, T> Page<T> toBean(Page<S> source, Class<T> targetType, Consumer<T> peek) {
// if (source == null) {
// return null;
// }
// List<T> list = toBean(source.getRecords(), targetType);
// if (peek != null) {
// list.forEach(peek);
// }
// return new Page<>(list, source.getTotal());
// }
public static <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType) {
return toBean(source, targetType, null);
}
public static <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType, Consumer<T> peek) {
if (source == null) {
return null;
}
List<T> list = toBean(source.getList(), targetType);
if (peek != null) {
list.forEach(peek);
}
return new PageResult<>(list, source.getTotal());
}
}

View File

@@ -0,0 +1,22 @@
package com.njcn.bpm.utils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* Key Value 的键值对
*
* @author 芋道源码
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class KeyValue<K, V> implements Serializable {
private K key;
private V value;
}

View File

@@ -0,0 +1,67 @@
package com.njcn.bpm.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjUtil;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**
* Map 工具类
*
* @author 芋道源码
*/
public class MapUtils {
/**
* 从哈希表表中,获得 keys 对应的所有 value 数组
*
* @param multimap 哈希表
* @param keys keys
* @return value 数组
*/
public static <K, V> List<V> getList(Multimap<K, V> multimap, Collection<K> keys) {
List<V> result = new ArrayList<>();
keys.forEach(k -> {
Collection<V> values = multimap.get(k);
if (CollectionUtil.isEmpty(values)) {
return;
}
result.addAll(values);
});
return result;
}
/**
* 从哈希表查找到 key 对应的 value然后进一步处理
* key 为 null 时, 不处理
* 注意,如果查找到的 value 为 null 时,不进行处理
*
* @param map 哈希表
* @param key key
* @param consumer 进一步处理的逻辑
*/
public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) {
if (ObjUtil.isNull(key) || CollUtil.isEmpty(map)) {
return;
}
V value = map.get(key);
if (value == null) {
return;
}
consumer.accept(value);
}
public static <K, V> Map<K, V> convertMap(List<KeyValue<K, V>> keyValues) {
Map<K, V> map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size());
keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue()));
return map;
}
}

View File

@@ -0,0 +1,63 @@
package com.njcn.bpm.utils;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.function.Consumer;
/**
* Object 工具类
*
* @author 芋道源码
*/
public class ObjectUtils {
/**
* 复制对象,并忽略 Id 编号
*
* @param object 被复制对象
* @param consumer 消费者,可以二次编辑被复制对象
* @return 复制后的对象
*/
public static <T> T cloneIgnoreId(T object, Consumer<T> consumer) {
T result = ObjectUtil.clone(object);
// 忽略 id 编号
Field field = ReflectUtil.getField(object.getClass(), "id");
if (field != null) {
ReflectUtil.setFieldValue(result, field, null);
}
// 二次编辑
if (result != null) {
consumer.accept(result);
}
return result;
}
public static <T extends Comparable<T>> T max(T obj1, T obj2) {
if (obj1 == null) {
return obj2;
}
if (obj2 == null) {
return obj1;
}
return obj1.compareTo(obj2) > 0 ? obj1 : obj2;
}
@SafeVarargs
public static <T> T defaultIfNull(T... array) {
for (T item : array) {
if (item != null) {
return item;
}
}
return null;
}
@SafeVarargs
public static <T> boolean equalsAny(T obj, T... array) {
return Arrays.asList(array).contains(obj);
}
}