工作流模块提交
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
package com.njcn.advance.pojo.vo.govern.voltage;
|
||||
|
||||
import com.njcn.advance.pojo.po.govern.voltage.SgSensitiveUnit;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -9,13 +9,9 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.advance.enums.AdvanceResponseEnum;
|
||||
import com.njcn.advance.mapper.govern.voltage.SgMachineMapper;
|
||||
import com.njcn.advance.pojo.param.govern.voltage.SgMachineParam;
|
||||
import com.njcn.advance.pojo.param.govern.voltage.SgProductLineParam;
|
||||
import com.njcn.advance.pojo.po.govern.voltage.SgMachine;
|
||||
import com.njcn.advance.pojo.po.govern.voltage.SgProductLine;
|
||||
import com.njcn.advance.pojo.po.govern.voltage.SgSensitiveUnit;
|
||||
import com.njcn.advance.pojo.po.govern.voltage.SgUser;
|
||||
import com.njcn.advance.pojo.vo.govern.voltage.SgMachineVO;
|
||||
import com.njcn.advance.pojo.vo.govern.voltage.SgProductLineVO;
|
||||
import com.njcn.advance.service.govern.voltage.ISgMachineService;
|
||||
import com.njcn.advance.service.govern.voltage.ISgSensitiveUnitService;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
|
||||
97
pqs-bpm/bpm-api/pom.xml
Normal file
97
pqs-bpm/bpm-api/pom.xml
Normal file
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>pqs</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>bpm-api</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<flowable.version>6.8.0</flowable.version>
|
||||
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
||||
<easy-trans.version>2.2.11</easy-trans.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>common-db</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>common-microservice</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Flowable 工作流相关 -->
|
||||
<dependency>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-spring-boot-starter-process</artifactId>
|
||||
<version>${flowable.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-spring-boot-starter-actuator</artifactId>
|
||||
<version>${flowable.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-jdk8</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fhs-opensource</groupId>
|
||||
<artifactId>easy-trans-spring-boot-starter</artifactId>
|
||||
<version>${easy-trans.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<groupId>org.springframework</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fhs-opensource</groupId>
|
||||
<artifactId>easy-trans-mybatis-plus-extend</artifactId>
|
||||
<version>${easy-trans.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fhs-opensource</groupId>
|
||||
<artifactId>easy-trans-anno</artifactId>
|
||||
<version>${easy-trans.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.njcn.bpm.enums;
|
||||
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
|
||||
/**
|
||||
* BPM 通用常量
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class BpmConstants {
|
||||
|
||||
/**
|
||||
* 流程实例的变量 - 状态
|
||||
*
|
||||
* @see ProcessInstance#getProcessVariables()
|
||||
*/
|
||||
public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS";
|
||||
/**
|
||||
* 流程实例的变量 - 发起用户选择的审批人 Map
|
||||
*
|
||||
* @see ProcessInstance#getProcessVariables()
|
||||
*/
|
||||
public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES";
|
||||
|
||||
/**
|
||||
* 任务的变量 - 状态
|
||||
*
|
||||
* @see org.flowable.task.api.Task#getTaskLocalVariables()
|
||||
*/
|
||||
public static final String TASK_VARIABLE_STATUS = "TASK_STATUS";
|
||||
/**
|
||||
* 任务的变量 - 理由
|
||||
*
|
||||
* 例如说:审批通过、不通过的理由
|
||||
*
|
||||
* @see org.flowable.task.api.Task#getTaskLocalVariables()
|
||||
*/
|
||||
public static final String TASK_VARIABLE_REASON = "TASK_REASON";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.njcn.bpm.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* BPM 模型的表单类型的枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum BpmModelFormTypeEnum implements IntArrayValuable {
|
||||
|
||||
NORMAL(10, "流程表单"), // 对应 BpmFormDO
|
||||
CUSTOM(20, "业务表单") // 业务自己定义的表单,自己进行数据的存储
|
||||
;
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmModelFormTypeEnum::getType).toArray();
|
||||
|
||||
private final Integer type;
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.njcn.bpm.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 异常处理类
|
||||
* @author qijian
|
||||
* @version 1.0.0
|
||||
* @date 2022年11月11日 09:56
|
||||
*/
|
||||
@Getter
|
||||
public enum BpmResponseEnum {
|
||||
|
||||
/**
|
||||
* 过程监督异常响应码的范围:
|
||||
* A00550 ~ A00649
|
||||
*/
|
||||
BPM_COMMON_ERROR("A00568","工作流模块异常"),
|
||||
|
||||
BPM_XML_ERROR("A00568","流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!"),
|
||||
|
||||
BPM_MODEL_REPEAT("A00568","流程标识已存在"),
|
||||
|
||||
BPM_MODEL_NOT_EXIST("A00568","流程模型不存在"),
|
||||
|
||||
PROCESS_DEFINITION_NOT_EXISTS("A00568","流程定义不存在"),
|
||||
|
||||
FORM_NOT_EXISTS("A00568","动态表单不存在"),
|
||||
|
||||
MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG("A00568","部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置"),
|
||||
|
||||
BPM_START_EVENT_NOT_EXIST("A00568","起始事件不存在"),
|
||||
|
||||
MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS("A00568","部署流程失败,原因:BPMN 流程图中,用户任务的名字不存在"),
|
||||
|
||||
REPEAT_NAME_FORM("A00568","流程表单名称重复"),
|
||||
|
||||
REPEAT_CATEGORY_NAME_FORM("A00568","流程类型名称重复"),
|
||||
|
||||
;
|
||||
|
||||
private final String code;
|
||||
|
||||
private final String message;
|
||||
|
||||
BpmResponseEnum(String code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.njcn.bpm.enums;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* BPM 任务的候选人策略枚举
|
||||
*
|
||||
* 例如说:分配给指定人审批
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum BpmTaskCandidateStrategyEnum {
|
||||
|
||||
ROLE(10, "角色"),
|
||||
DEPT_MEMBER(20, "部门的成员"), // 包括负责人
|
||||
DEPT_LEADER(21, "部门的负责人"),
|
||||
POST(22, "岗位"),
|
||||
USER(30, "用户"),
|
||||
START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人
|
||||
USER_GROUP(40, "用户组"),
|
||||
EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager
|
||||
;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final Integer strategy;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
public static BpmTaskCandidateStrategyEnum valueOf(Integer strategy) {
|
||||
return ArrayUtil.firstMatch(o -> o.getStrategy().equals(strategy), values());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.njcn.bpm.enums;
|
||||
|
||||
/**
|
||||
* BPMN XML 常量信息
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface BpmnModelConstants {
|
||||
|
||||
String BPMN_FILE_SUFFIX = ".bpmn";
|
||||
|
||||
/**
|
||||
* BPMN 中的命名空间
|
||||
*/
|
||||
String NAMESPACE = "http://flowable.org/bpmn";
|
||||
|
||||
/**
|
||||
* BPMN UserTask 的扩展属性,用于标记候选人策略
|
||||
*/
|
||||
String USER_TASK_CANDIDATE_STRATEGY = "candidateStrategy";
|
||||
/**
|
||||
* BPMN UserTask 的扩展属性,用于标记候选人参数
|
||||
*/
|
||||
String USER_TASK_CANDIDATE_PARAM = "candidateParam";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.njcn.bpm.enums;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 错误码对象
|
||||
*
|
||||
* 全局错误码,占用 [0, 999], 参见 {@link GlobalErrorCodeConstants}
|
||||
*
|
||||
* TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备
|
||||
*/
|
||||
@Data
|
||||
public class ErrorCode {
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private final Integer code;
|
||||
/**
|
||||
* 错误提示
|
||||
*/
|
||||
private final String msg;
|
||||
|
||||
public ErrorCode(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.msg = message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.njcn.bpm.enums;
|
||||
|
||||
|
||||
/**
|
||||
* 全局错误码枚举
|
||||
* 0-999 系统异常编码保留
|
||||
*
|
||||
* 一般情况下,使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
|
||||
* 虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的
|
||||
* 比较特殊的是,因为之前一直使用 0 作为成功,就不使用 200 啦。
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface GlobalErrorCodeConstants {
|
||||
|
||||
ErrorCode SUCCESS = new ErrorCode(0, "成功");
|
||||
|
||||
// ========== 客户端错误段 ==========
|
||||
|
||||
ErrorCode BAD_REQUEST = new ErrorCode(400, "请求参数不正确");
|
||||
ErrorCode UNAUTHORIZED = new ErrorCode(401, "账号未登录");
|
||||
ErrorCode FORBIDDEN = new ErrorCode(403, "没有该操作权限");
|
||||
ErrorCode NOT_FOUND = new ErrorCode(404, "请求未找到");
|
||||
ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确");
|
||||
ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许
|
||||
ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试");
|
||||
|
||||
// ========== 服务端错误段 ==========
|
||||
|
||||
ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常");
|
||||
ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启");
|
||||
ErrorCode ERROR_CONFIGURATION = new ErrorCode(502, "错误的配置项");
|
||||
|
||||
// ========== 自定义错误段 ==========
|
||||
ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求
|
||||
ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作");
|
||||
|
||||
ErrorCode UNKNOWN = new ErrorCode(999, "未知错误");
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.njcn.bpm.enums;
|
||||
|
||||
/**
|
||||
* 可生成 Int 数组的接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IntArrayValuable {
|
||||
|
||||
/**
|
||||
* @return int 数组
|
||||
*/
|
||||
int[] array();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.njcn.bpm.pojo.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* BPM 流程 MetaInfo Response DTO
|
||||
* 主要用于 { Model#setMetaInfo(String)} 的存储
|
||||
*
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class BpmModelMetaInfoRespDTO {
|
||||
|
||||
/**
|
||||
* 流程图标
|
||||
*/
|
||||
private String icon;
|
||||
/**
|
||||
* 流程描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 表单类型
|
||||
*/
|
||||
private Integer formType;
|
||||
/**
|
||||
* 表单编号
|
||||
*/
|
||||
private String formId;
|
||||
/**
|
||||
* 自定义表单的提交路径,使用 Vue 的路由地址
|
||||
*/
|
||||
private String formCustomCreatePath;
|
||||
/**
|
||||
* 自定义表单的查看路径,使用 Vue 的路由地址
|
||||
*/
|
||||
private String formCustomViewPath;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.njcn.bpm.pojo.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.njcn.bpm.enums.ErrorCode;
|
||||
import com.njcn.bpm.enums.GlobalErrorCodeConstants;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import lombok.Data;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 通用返回
|
||||
*
|
||||
* @param <T> 数据泛型
|
||||
*/
|
||||
@Data
|
||||
public class CommonResult<T> implements Serializable {
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*
|
||||
* @see ErrorCode#getCode()
|
||||
*/
|
||||
private Integer code;
|
||||
/**
|
||||
* 返回数据
|
||||
*/
|
||||
private T data;
|
||||
/**
|
||||
* 错误提示,用户可阅读
|
||||
*
|
||||
* @see ErrorCode#getMsg() ()
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 将传入的 result 对象,转换成另外一个泛型结果的对象
|
||||
*
|
||||
* 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
|
||||
*
|
||||
* @param result 传入的 result 对象
|
||||
* @param <T> 返回的泛型
|
||||
* @return 新的 CommonResult 对象
|
||||
*/
|
||||
public static <T> CommonResult<T> error(CommonResult<?> result) {
|
||||
return error(result.getCode(), result.getMsg());
|
||||
}
|
||||
|
||||
public static <T> CommonResult<T> error(Integer code, String message) {
|
||||
Assert.isTrue(!GlobalErrorCodeConstants.SUCCESS.getCode().equals(code), "code 必须是错误的!");
|
||||
CommonResult<T> result = new CommonResult<>();
|
||||
result.code = code;
|
||||
result.msg = message;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T> CommonResult<T> error( String message) {
|
||||
CommonResult<T> result = new CommonResult<>();
|
||||
result.msg = message;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T> CommonResult<T> error(ErrorCode errorCode) {
|
||||
return error(errorCode.getCode(), errorCode.getMsg());
|
||||
}
|
||||
|
||||
public static <T> CommonResult<T> success(T data) {
|
||||
CommonResult<T> result = new CommonResult<>();
|
||||
result.code = GlobalErrorCodeConstants.SUCCESS.getCode();
|
||||
result.data = data;
|
||||
result.msg = "";
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isSuccess(Integer code) {
|
||||
return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode());
|
||||
}
|
||||
|
||||
@JsonIgnore // 避免 jackson 序列化
|
||||
public boolean isSuccess() {
|
||||
return isSuccess(code);
|
||||
}
|
||||
|
||||
@JsonIgnore // 避免 jackson 序列化
|
||||
public boolean isError() {
|
||||
return !isSuccess();
|
||||
}
|
||||
|
||||
// ========= 和 Exception 异常体系集成 =========
|
||||
|
||||
/**
|
||||
*/
|
||||
public void checkError() throws BusinessException {
|
||||
if (isSuccess()) {
|
||||
return;
|
||||
}
|
||||
// 业务异常
|
||||
throw new BusinessException(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果没有,则返回 {@link #data} 数据
|
||||
*/
|
||||
@JsonIgnore // 避免 jackson 序列化
|
||||
public T getCheckedData() {
|
||||
checkError();
|
||||
return data;
|
||||
}
|
||||
|
||||
public static <T> CommonResult<T> error(BusinessException serviceException) {
|
||||
return error( serviceException.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.njcn.bpm.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "分页结果")
|
||||
@Data
|
||||
public final class PageResult<T> implements Serializable {
|
||||
|
||||
private List<T> list;
|
||||
|
||||
private Long total;
|
||||
|
||||
public PageResult() {
|
||||
}
|
||||
|
||||
public PageResult(List<T> list, Long total) {
|
||||
this.list = list;
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public PageResult(Long total) {
|
||||
this.list = new ArrayList<>();
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public static <T> PageResult<T> empty() {
|
||||
return new PageResult<>(0L);
|
||||
}
|
||||
|
||||
public static <T> PageResult<T> empty(Long total) {
|
||||
return new PageResult<>(total);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.njcn.bpm.pojo.param;
|
||||
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.web.constant.ValidMessage;
|
||||
import com.njcn.web.pojo.param.BaseParam;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.*;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* BPM 流程分类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class BpmCategoryParam implements Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* 分类名
|
||||
*/
|
||||
@ApiModelProperty("分类名")
|
||||
@NotNull(message = "分类名不能为空")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 分类标志
|
||||
*/
|
||||
@ApiModelProperty("分类标志")
|
||||
@NotNull(message = "分类标志不能为空")
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 分类描述
|
||||
*/
|
||||
@ApiModelProperty("分类描述")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 分类状态
|
||||
*
|
||||
*/
|
||||
@ApiModelProperty("分类状态")
|
||||
private Integer status = 1;
|
||||
/**
|
||||
* 分类排序
|
||||
*/
|
||||
@ApiModelProperty("分类排序")
|
||||
private Integer sort = 100;
|
||||
|
||||
/**
|
||||
* 更新操作实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class BpmCategoryUpdateParam extends BpmCategoryParam {
|
||||
|
||||
/**
|
||||
* 表Id
|
||||
*/
|
||||
@ApiModelProperty("id")
|
||||
@NotBlank(message = ValidMessage.ID_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = ValidMessage.ID_FORMAT_ERROR)
|
||||
private String id;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分页查询实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class BpmCategoryQueryParam extends BaseParam {
|
||||
|
||||
/**
|
||||
* 表单名称
|
||||
*/
|
||||
@ApiModelProperty("分类名称")
|
||||
private String name;
|
||||
|
||||
|
||||
/**
|
||||
* 分类标志
|
||||
*/
|
||||
@ApiModelProperty("分类标志")
|
||||
private String code;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.njcn.bpm.pojo.param;
|
||||
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.web.constant.ValidMessage;
|
||||
import com.njcn.web.pojo.param.BaseParam;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class BpmFormParam implements Serializable {
|
||||
|
||||
/**
|
||||
* 表单名
|
||||
*/
|
||||
@ApiModelProperty("表单名称")
|
||||
@NotNull(message = "表单名称不能为空")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@ApiModelProperty("表单状态")
|
||||
@NotNull(message = "表单状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 表单的配置
|
||||
*/
|
||||
@ApiModelProperty("表单的配置-JSON 字符串")
|
||||
@NotNull(message = "表单的配置不能为空")
|
||||
private String conf;
|
||||
|
||||
/**
|
||||
* 表单项的数组
|
||||
*
|
||||
*/
|
||||
@ApiModelProperty("表单项的数组-JSON 字符串的数组")
|
||||
@NotNull(message = "表单项的数组不能为空")
|
||||
private List<String> fields;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@ApiModelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
|
||||
/**
|
||||
* 更新操作实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class BpmFormUpdateParam extends BpmFormParam {
|
||||
|
||||
/**
|
||||
* 表Id
|
||||
*/
|
||||
@ApiModelProperty("id")
|
||||
@NotBlank(message = ValidMessage.ID_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = ValidMessage.ID_FORMAT_ERROR)
|
||||
private String id;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分页查询实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class BpmFormQueryParam extends BaseParam {
|
||||
|
||||
/**
|
||||
* 表单名称
|
||||
*/
|
||||
@ApiModelProperty("表单名称")
|
||||
private String name;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.njcn.bpm.pojo.param;
|
||||
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.web.constant.ValidMessage;
|
||||
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;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
@Data
|
||||
public class BpmModelParam implements Serializable {
|
||||
|
||||
|
||||
@ApiModelProperty("流程标识")
|
||||
@NotNull(message = "流程标识不能为空")
|
||||
private String key;
|
||||
|
||||
@ApiModelProperty("流程名称")
|
||||
@NotNull(message = "流程名称不能为空")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty( "流程图标")
|
||||
private String icon;
|
||||
|
||||
@ApiModelProperty("流程分类")
|
||||
private String category;
|
||||
|
||||
@ApiModelProperty("BPMN XML")
|
||||
private String bpmnXml;
|
||||
|
||||
@ApiModelProperty("流程表单")
|
||||
private String formId;
|
||||
|
||||
@ApiModelProperty("流程描述")
|
||||
private String description;
|
||||
|
||||
private Integer formType = 10;
|
||||
|
||||
|
||||
private String formCustomCreatePath;
|
||||
|
||||
private String formCustomViewPath;
|
||||
|
||||
/**
|
||||
* 更新操作实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class BpmModelUpdateParam extends BpmModelParam {
|
||||
|
||||
/**
|
||||
* 表Id
|
||||
*/
|
||||
@ApiModelProperty("id")
|
||||
@NotBlank(message = ValidMessage.ID_NOT_BLANK)
|
||||
private String id;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分页查询实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class BpmModelQueryParam extends BaseParam {
|
||||
|
||||
@ApiModelProperty("流程标识")
|
||||
private String key;
|
||||
|
||||
@ApiModelProperty("流程名称")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty("流程分类")
|
||||
private String category;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.njcn.bpm.pojo.param;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.db.bo.BaseEntity;
|
||||
import com.njcn.web.constant.ValidMessage;
|
||||
import com.njcn.web.pojo.param.BaseParam;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BPM 流程定义的拓信息
|
||||
* 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表
|
||||
*/
|
||||
@Data
|
||||
public class BpmProcessDefinitionInfoParam extends BaseEntity implements Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* 流程定义的编号
|
||||
* <p>
|
||||
* 关联 ProcessDefinition 的 id 属性
|
||||
*/
|
||||
private String processDefinitionId;
|
||||
/**
|
||||
* 流程模型的编号
|
||||
* <p>
|
||||
* 关联 Model 的 id 属性
|
||||
*/
|
||||
private String modelId;
|
||||
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
private String icon;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 表单类型
|
||||
*/
|
||||
private Integer formType;
|
||||
|
||||
/**
|
||||
* 动态表单编号
|
||||
*/
|
||||
private String formId;
|
||||
|
||||
/**
|
||||
* 表单的配置
|
||||
*/
|
||||
private String formConf;
|
||||
|
||||
/**
|
||||
* 表单项的数组
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<String> formFields;
|
||||
|
||||
|
||||
/**
|
||||
* 自定义表单的提交路径,使用 Vue 的路由地址
|
||||
*/
|
||||
private String formCustomCreatePath;
|
||||
|
||||
/**
|
||||
* 自定义表单的查看路径,使用 Vue 的路由地址
|
||||
*/
|
||||
private String formCustomViewPath;
|
||||
|
||||
/**
|
||||
* 更新操作实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class BpmProcessDefinitionInfoUpdateParam extends BpmProcessDefinitionInfoParam {
|
||||
|
||||
/**
|
||||
* 表Id
|
||||
*/
|
||||
@ApiModelProperty("id")
|
||||
@NotBlank(message = ValidMessage.ID_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = ValidMessage.ID_FORMAT_ERROR)
|
||||
private String id;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分页查询实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class BpmProcessDefinitionInfoQueryParam extends BaseParam {
|
||||
|
||||
@ApiModelProperty("标识-精准匹配")
|
||||
private String key;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.njcn.bpm.pojo.param;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Schema(description="分页参数")
|
||||
@Data
|
||||
public class PageParam implements Serializable {
|
||||
|
||||
private static final Integer PAGE_NO = 1;
|
||||
private static final Integer PAGE_SIZE = 10;
|
||||
|
||||
/**
|
||||
* 每页条数 - 不分页
|
||||
*
|
||||
* 例如说,导出接口,可以设置 {@link #pageSize} 为 -1 不分页,查询所有数据。
|
||||
*/
|
||||
public static final Integer PAGE_SIZE_NONE = -1;
|
||||
|
||||
@NotNull(message = "页码不能为空")
|
||||
@Min(value = 1, message = "页码最小值为 1")
|
||||
private Integer pageNo = PAGE_NO;
|
||||
|
||||
@NotNull(message = "每页条数不能为空")
|
||||
@Min(value = 1, message = "每页条数最小值为 1")
|
||||
@Max(value = 100, message = "每页条数最大值为 100")
|
||||
private Integer pageSize = PAGE_SIZE;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.njcn.bpm.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.db.bo.BaseEntity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* BPM 流程分类 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("bpm_category")
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BpmCategory extends BaseEntity implements Serializable {
|
||||
|
||||
/**
|
||||
* 分类编号
|
||||
*/
|
||||
@TableId
|
||||
private String id;
|
||||
/**
|
||||
* 分类名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 分类标志
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 分类描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 分类状态
|
||||
*
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 分类排序
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 状态:0-删除 1-正常
|
||||
*/
|
||||
private Integer state;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.njcn.bpm.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import com.njcn.db.bo.BaseEntity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BPM 工作流的表单定义
|
||||
* 用于工作流的申请表单,需要动态配置的场景
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName(value = "bpm_form", autoResultMap = true)
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BpmForm extends BaseEntity implements Serializable {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private String id;
|
||||
/**
|
||||
* 表单名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 表单的配置
|
||||
*/
|
||||
private String conf;
|
||||
/**
|
||||
* 表单项的数组
|
||||
*
|
||||
* 目前直接将 https://github.com/JakHuang/form-generator 生成的 JSON 串,直接保存
|
||||
* 定义:https://github.com/JakHuang/form-generator/issues/46
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<String> fields;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 状态:0-删除 1-正常
|
||||
*/
|
||||
private Integer state;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.njcn.bpm.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import com.njcn.db.bo.BaseEntity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BPM 流程定义的拓信息
|
||||
* 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表
|
||||
*
|
||||
*/
|
||||
@TableName(value = "bpm_process_definition_info", autoResultMap = true)
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BpmProcessDefinitionInfo extends BaseEntity implements Serializable {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private String id;
|
||||
/**
|
||||
* 流程定义的编号
|
||||
*
|
||||
* 关联 ProcessDefinition 的 id 属性
|
||||
*/
|
||||
private String processDefinitionId;
|
||||
/**
|
||||
* 流程模型的编号
|
||||
*
|
||||
* 关联 Model 的 id 属性
|
||||
*/
|
||||
private String modelId;
|
||||
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
private String icon;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 表单类型
|
||||
*
|
||||
*/
|
||||
private Integer formType;
|
||||
|
||||
/**
|
||||
* 动态表单编号
|
||||
*
|
||||
*/
|
||||
private String formId;
|
||||
|
||||
/**
|
||||
* 表单的配置
|
||||
*
|
||||
*/
|
||||
private String formConf;
|
||||
|
||||
/**
|
||||
* 表单项的数组
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<String> formFields;
|
||||
|
||||
|
||||
/**
|
||||
* 自定义表单的提交路径,使用 Vue 的路由地址
|
||||
*/
|
||||
private String formCustomCreatePath;
|
||||
|
||||
/**
|
||||
* 自定义表单的查看路径,使用 Vue 的路由地址
|
||||
*/
|
||||
private String formCustomViewPath;
|
||||
|
||||
/**
|
||||
* 状态:0-删除 1-正常
|
||||
*/
|
||||
private Integer state;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.njcn.bpm.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.db.bo.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 流程表单
|
||||
* </p>
|
||||
*
|
||||
* @author hongawen
|
||||
* @since 2024-04-25
|
||||
*/
|
||||
@Data
|
||||
@TableName("wf_form")
|
||||
public class WFForm extends BaseEntity implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 流程表单id
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 表单名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 表单内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 表单描述
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 状态:0-删除 1-正常
|
||||
*/
|
||||
private Integer state;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.njcn.bpm.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* BPM 流程分类 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class BpmCategoryVO implements Serializable {
|
||||
|
||||
/**
|
||||
* 分类编号
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 分类名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 分类标志
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 分类描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 分类状态
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 分类排序
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.njcn.bpm.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class BpmFormVO implements Serializable {
|
||||
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String conf;
|
||||
|
||||
private List<String> fields;
|
||||
|
||||
private Integer status; // 参见 CommonStatusEnum 枚举
|
||||
|
||||
private String remark;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.njcn.bpm.pojo.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 流程模型 Response VO")
|
||||
@Data
|
||||
public class BpmModelRespVO {
|
||||
|
||||
private String id;
|
||||
|
||||
private String key;
|
||||
|
||||
private String name;
|
||||
|
||||
@Schema(description = "流程图标", example = "https://www.iocoder.cn/yudao.jpg")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "流程描述", example = "我是描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "流程分类编码", example = "1")
|
||||
private String category;
|
||||
@Schema(description = "流程分类名字", example = "请假")
|
||||
private String categoryName;
|
||||
|
||||
@Schema(description = "表单类型-参见 bpm_model_form_type 数据字典", example = "1")
|
||||
private Integer formType;
|
||||
|
||||
@Schema(description = "表单编号", example = "1024")
|
||||
private String formId; // 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空
|
||||
@Schema(description = "表单名字", example = "请假表单")
|
||||
private String formName;
|
||||
|
||||
@Schema(description = "自定义表单的提交路径", example = "/bpm/oa/leave/create")
|
||||
private String formCustomCreatePath; // 使用 Vue 的路由地址-在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空
|
||||
@Schema(description = "自定义表单的查看路径", example = "/bpm/oa/leave/view")
|
||||
private String formCustomViewPath; // ,使用 Vue 的路由地址-在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空
|
||||
|
||||
private LocalDateTime createTime;
|
||||
|
||||
private String bpmnXml;
|
||||
|
||||
/**
|
||||
* 最新部署的流程定义
|
||||
*/
|
||||
private BpmProcessDefinitionInfoVO processDefinition;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.njcn.bpm.pojo.vo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import com.njcn.db.bo.BaseEntity;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BPM 流程定义的拓信息
|
||||
* 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表
|
||||
*
|
||||
*/
|
||||
@Data
|
||||
public class BpmProcessDefinitionInfoVO extends BaseEntity implements Serializable {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 流程定义的编号
|
||||
*
|
||||
* 关联 ProcessDefinition 的 id 属性
|
||||
*/
|
||||
private String processDefinitionId;
|
||||
/**
|
||||
* 流程模型的编号
|
||||
*
|
||||
* 关联 Model 的 id 属性
|
||||
*/
|
||||
private String modelId;
|
||||
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
private String icon;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 表单类型
|
||||
*
|
||||
*/
|
||||
private Integer formType;
|
||||
|
||||
/**
|
||||
* 动态表单编号
|
||||
*
|
||||
*/
|
||||
private String formId;
|
||||
|
||||
/**
|
||||
* 表单的配置
|
||||
*
|
||||
*/
|
||||
private String formConf;
|
||||
|
||||
/**
|
||||
* 表单项的数组
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<String> formFields;
|
||||
|
||||
|
||||
/**
|
||||
* 自定义表单的提交路径,使用 Vue 的路由地址
|
||||
*/
|
||||
private String formCustomCreatePath;
|
||||
|
||||
/**
|
||||
* 自定义表单的查看路径,使用 Vue 的路由地址
|
||||
*/
|
||||
private String formCustomViewPath;
|
||||
|
||||
|
||||
private Integer version;
|
||||
|
||||
private String name;
|
||||
|
||||
private String key;
|
||||
|
||||
|
||||
@ApiModelProperty("流程分类")
|
||||
private String category;
|
||||
|
||||
@ApiModelProperty("流程分类名字")
|
||||
private String categoryName;
|
||||
|
||||
|
||||
@ApiModelProperty("表单名字")
|
||||
private String formName;
|
||||
|
||||
|
||||
private Integer suspensionState; // 参见 SuspensionState 枚举
|
||||
|
||||
@ApiModelProperty("部署时间")
|
||||
private LocalDateTime deploymentTime; // 需要从对应的 Deployment 读取,非必须返回
|
||||
|
||||
@ApiModelProperty("BPMN")
|
||||
private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回
|
||||
|
||||
@ApiModelProperty("发起用户需要选择审批人的任务数组")
|
||||
private List<BpmProcessDefinitionInfoVO.UserTask> startUserSelectTasks; // 需要从对应的 BpmnModel 读取,非必须返回
|
||||
|
||||
@Schema(description = "BPMN UserTask 用户任务")
|
||||
@Data
|
||||
public static class UserTask {
|
||||
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Bean 工具类
|
||||
*
|
||||
* 1. 默认使用 {@link BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能
|
||||
* 2. 针对复杂的对象转换,可以搜参考 AuthConvert 实现,通过 mapstruct + default 配合实现
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class BeanUtils {
|
||||
|
||||
public static <T> T toBean(Object source, Class<T> targetClass) {
|
||||
return BeanUtil.toBean(source, targetClass);
|
||||
}
|
||||
|
||||
public static <T> T toBean(Object source, Class<T> targetClass, Consumer<T> peek) {
|
||||
T target = toBean(source, targetClass);
|
||||
if (target != null) {
|
||||
peek.accept(target);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
public static <S, T> List<T> toBean(List<S> source, Class<T> targetType) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
return CollectionUtils.convertList(source, s -> toBean(s, targetType));
|
||||
}
|
||||
|
||||
public static <S, T> List<T> toBean(List<S> source, Class<T> targetType, Consumer<T> peek) {
|
||||
List<T> list = toBean(source, targetType);
|
||||
if (list != null) {
|
||||
list.forEach(peek);
|
||||
}
|
||||
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());
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class CollectionUtils {
|
||||
|
||||
public static boolean containsAny(Object source, Object... targets) {
|
||||
return asList(targets).contains(source);
|
||||
}
|
||||
|
||||
public static boolean isAnyEmpty(Collection<?>... collections) {
|
||||
return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty);
|
||||
}
|
||||
|
||||
public static <T> boolean anyMatch(Collection<T> from, Predicate<T> predicate) {
|
||||
return from.stream().anyMatch(predicate);
|
||||
}
|
||||
|
||||
public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return from.stream().filter(predicate).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return distinct(from, keyMapper, (t1, t2) -> t1);
|
||||
}
|
||||
|
||||
public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper, BinaryOperator<T> cover) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values());
|
||||
}
|
||||
|
||||
public static <T, U> List<U> convertList(T[] from, Function<T, U> func) {
|
||||
if (ArrayUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return convertList(Arrays.asList(from), func);
|
||||
}
|
||||
|
||||
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T, U> List<U> convertListByFlatMap(Collection<T> from,
|
||||
Function<T, ? extends Stream<? extends U>> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T, U, R> List<R> convertListByFlatMap(Collection<T> from,
|
||||
Function<? super T, ? extends U> mapper,
|
||||
Function<U, ? extends Stream<? extends R>> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <K, V> List<V> mergeValuesFromMap(Map<K, List<V>> map) {
|
||||
return map.values()
|
||||
.stream()
|
||||
.flatMap(List::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T> Set<T> convertSet(Collection<T> from) {
|
||||
return convertSet(from, v -> v);
|
||||
}
|
||||
|
||||
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, T> convertMapByFilter(Collection<T> from, Predicate<T> filter, Function<T, K> keyFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return from.stream().filter(filter).collect(Collectors.toMap(keyFunc, v -> v));
|
||||
}
|
||||
|
||||
public static <T, U> Set<U> convertSetByFlatMap(Collection<T> from,
|
||||
Function<T, ? extends Stream<? extends U>> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static <T, U, R> Set<R> convertSetByFlatMap(Collection<T> from,
|
||||
Function<? super T, ? extends U> mapper,
|
||||
Function<U, ? extends Stream<? extends R>> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return convertMap(from, keyFunc, Function.identity());
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc, Supplier<? extends Map<K, T>> supplier) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return supplier.get();
|
||||
}
|
||||
return convertMap(from, keyFunc, Function.identity(), supplier);
|
||||
}
|
||||
|
||||
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1);
|
||||
}
|
||||
|
||||
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new);
|
||||
}
|
||||
|
||||
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, Supplier<? extends Map<K, V>> supplier) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return supplier.get();
|
||||
}
|
||||
return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier);
|
||||
}
|
||||
|
||||
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction, Supplier<? extends Map<K, V>> supplier) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier));
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, List<T>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList())));
|
||||
}
|
||||
|
||||
public static <T, K, V> Map<K, List<V>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return from.stream()
|
||||
.collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList())));
|
||||
}
|
||||
|
||||
// 暂时没想好名字,先以 2 结尾噶
|
||||
public static <T, K, V> Map<K, Set<V>> convertMultiMap2(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet())));
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, T> convertImmutableMap(Collection<T> from, Function<T, K> keyFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
ImmutableMap.Builder<K, T> builder = ImmutableMap.builder();
|
||||
from.forEach(item -> builder.put(keyFunc.apply(item), item));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对比老、新两个列表,找出新增、修改、删除的数据
|
||||
*
|
||||
* @param oldList 老列表
|
||||
* @param newList 新列表
|
||||
* @param sameFunc 对比函数,返回 true 表示相同,返回 false 表示不同
|
||||
* 注意,same 是通过每个元素的“标识”,判断它们是不是同一个数据
|
||||
* @return [新增列表、修改列表、删除列表]
|
||||
*/
|
||||
public static <T> List<List<T>> diffList(Collection<T> oldList, Collection<T> newList,
|
||||
BiFunction<T, T, Boolean> sameFunc) {
|
||||
List<T> createList = new LinkedList<>(newList); // 默认都认为是新增的,后续会进行移除
|
||||
List<T> updateList = new ArrayList<>();
|
||||
List<T> deleteList = new ArrayList<>();
|
||||
|
||||
// 通过以 oldList 为主遍历,找出 updateList 和 deleteList
|
||||
for (T oldObj : oldList) {
|
||||
// 1. 寻找是否有匹配的
|
||||
T foundObj = null;
|
||||
for (Iterator<T> iterator = createList.iterator(); iterator.hasNext(); ) {
|
||||
T newObj = iterator.next();
|
||||
// 1.1 不匹配,则直接跳过
|
||||
if (!sameFunc.apply(oldObj, newObj)) {
|
||||
continue;
|
||||
}
|
||||
// 1.2 匹配,则移除,并结束寻找
|
||||
iterator.remove();
|
||||
foundObj = newObj;
|
||||
break;
|
||||
}
|
||||
// 2. 匹配添加到 updateList;不匹配则添加到 deleteList 中
|
||||
if (foundObj != null) {
|
||||
updateList.add(foundObj);
|
||||
} else {
|
||||
deleteList.add(oldObj);
|
||||
}
|
||||
}
|
||||
return asList(createList, updateList, deleteList);
|
||||
}
|
||||
|
||||
public static boolean containsAny(Collection<?> source, Collection<?> candidates) {
|
||||
return org.springframework.util.CollectionUtils.containsAny(source, candidates);
|
||||
}
|
||||
|
||||
public static <T> T getFirst(List<T> from) {
|
||||
return !CollectionUtil.isEmpty(from) ? from.get(0) : null;
|
||||
}
|
||||
|
||||
public static <T> T findFirst(Collection<T> from, Predicate<T> predicate) {
|
||||
return findFirst(from, predicate, Function.identity());
|
||||
}
|
||||
|
||||
public static <T, U> U findFirst(Collection<T> from, Predicate<T> predicate, Function<T, U> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return null;
|
||||
}
|
||||
return from.stream().filter(predicate).findFirst().map(func).orElse(null);
|
||||
}
|
||||
|
||||
public static <T, V extends Comparable<? super V>> V getMaxValue(Collection<T> from, Function<T, V> valueFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return null;
|
||||
}
|
||||
assert !from.isEmpty(); // 断言,避免告警
|
||||
T t = from.stream().max(Comparator.comparing(valueFunc)).get();
|
||||
return valueFunc.apply(t);
|
||||
}
|
||||
|
||||
public static <T, V extends Comparable<? super V>> V getMinValue(List<T> from, Function<T, V> valueFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return null;
|
||||
}
|
||||
assert from.size() > 0; // 断言,避免告警
|
||||
T t = from.stream().min(Comparator.comparing(valueFunc)).get();
|
||||
return valueFunc.apply(t);
|
||||
}
|
||||
|
||||
public static <T, V extends Comparable<? super V>> V getSumValue(List<T> from, Function<T, V> valueFunc,
|
||||
BinaryOperator<V> accumulator) {
|
||||
return getSumValue(from, valueFunc, accumulator, null);
|
||||
}
|
||||
|
||||
public static <T, V extends Comparable<? super V>> V getSumValue(Collection<T> from, Function<T, V> valueFunc,
|
||||
BinaryOperator<V> accumulator, V defaultValue) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return defaultValue;
|
||||
}
|
||||
assert !from.isEmpty(); // 断言,避免告警
|
||||
return from.stream().map(valueFunc).filter(Objects::nonNull).reduce(accumulator).orElse(defaultValue);
|
||||
}
|
||||
|
||||
public static <T> void addIfNotNull(Collection<T> coll, T item) {
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
coll.add(item);
|
||||
}
|
||||
|
||||
public static <T> Collection<T> singleton(T obj) {
|
||||
return obj == null ? Collections.emptyList() : Collections.singleton(obj);
|
||||
}
|
||||
|
||||
public static <T> List<T> newArrayList(List<List<T>> list) {
|
||||
return list.stream().flatMap(Collection::stream).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
149
pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/DateUtils.java
Normal file
149
pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/DateUtils.java
Normal file
@@ -0,0 +1,149 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
|
||||
import java.time.*;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 时间工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class DateUtils {
|
||||
|
||||
/**
|
||||
* 时区 - 默认
|
||||
*/
|
||||
public static final String TIME_ZONE_DEFAULT = "GMT+8";
|
||||
|
||||
/**
|
||||
* 秒转换成毫秒
|
||||
*/
|
||||
public static final long SECOND_MILLIS = 1000;
|
||||
|
||||
public static final String FORMAT_YEAR_MONTH_DAY = "yyyy-MM-dd";
|
||||
|
||||
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
/**
|
||||
* 将 LocalDateTime 转换成 Date
|
||||
*
|
||||
* @param date LocalDateTime
|
||||
* @return LocalDateTime
|
||||
*/
|
||||
public static Date of(LocalDateTime date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
// 将此日期时间与时区相结合以创建 ZonedDateTime
|
||||
ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault());
|
||||
// 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳
|
||||
Instant instant = zonedDateTime.toInstant();
|
||||
// UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
|
||||
return Date.from(instant);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Date 转换成 LocalDateTime
|
||||
*
|
||||
* @param date Date
|
||||
* @return LocalDateTime
|
||||
*/
|
||||
public static LocalDateTime of(Date date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
// 转为时间戳
|
||||
Instant instant = date.toInstant();
|
||||
// UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
|
||||
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
public static Date addTime(Duration duration) {
|
||||
return new Date(System.currentTimeMillis() + duration.toMillis());
|
||||
}
|
||||
|
||||
public static boolean isExpired(LocalDateTime time) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
return now.isAfter(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定时间
|
||||
*
|
||||
* @param year 年
|
||||
* @param mouth 月
|
||||
* @param day 日
|
||||
* @return 指定时间
|
||||
*/
|
||||
public static Date buildTime(int year, int mouth, int day) {
|
||||
return buildTime(year, mouth, day, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定时间
|
||||
*
|
||||
* @param year 年
|
||||
* @param mouth 月
|
||||
* @param day 日
|
||||
* @param hour 小时
|
||||
* @param minute 分钟
|
||||
* @param second 秒
|
||||
* @return 指定时间
|
||||
*/
|
||||
public static Date buildTime(int year, int mouth, int day,
|
||||
int hour, int minute, int second) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.YEAR, year);
|
||||
calendar.set(Calendar.MONTH, mouth - 1);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, day);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, hour);
|
||||
calendar.set(Calendar.MINUTE, minute);
|
||||
calendar.set(Calendar.SECOND, second);
|
||||
calendar.set(Calendar.MILLISECOND, 0); // 一般情况下,都是 0 毫秒
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
public static Date max(Date a, Date b) {
|
||||
if (a == null) {
|
||||
return b;
|
||||
}
|
||||
if (b == null) {
|
||||
return a;
|
||||
}
|
||||
return a.compareTo(b) > 0 ? a : b;
|
||||
}
|
||||
|
||||
public static LocalDateTime max(LocalDateTime a, LocalDateTime b) {
|
||||
if (a == null) {
|
||||
return b;
|
||||
}
|
||||
if (b == null) {
|
||||
return a;
|
||||
}
|
||||
return a.isAfter(b) ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否今天
|
||||
*
|
||||
* @param date 日期
|
||||
* @return 是否
|
||||
*/
|
||||
public static boolean isToday(LocalDateTime date) {
|
||||
return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否昨天
|
||||
*
|
||||
* @param date 日期
|
||||
* @return 是否
|
||||
*/
|
||||
public static boolean isYesterday(LocalDateTime date) {
|
||||
return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now().minusDays(1));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 数字的工具类,补全 {@link NumberUtil} 的功能
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class NumberUtils {
|
||||
|
||||
public static Long parseLong(String str) {
|
||||
return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null;
|
||||
}
|
||||
|
||||
public static Integer parseInt(String str) {
|
||||
return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过经纬度获取地球上两点之间的距离
|
||||
*
|
||||
* 参考 <<a href="https://gitee.com/dromara/hutool/blob/1caabb586b1f95aec66a21d039c5695df5e0f4c1/hutool-core/src/main/java/cn/hutool/core/util/DistanceUtil.java">DistanceUtil</a>> 实现,目前它已经被 hutool 删除
|
||||
*
|
||||
* @param lat1 经度1
|
||||
* @param lng1 纬度1
|
||||
* @param lat2 经度2
|
||||
* @param lng2 纬度2
|
||||
* @return 距离,单位:千米
|
||||
*/
|
||||
public static double getDistance(double lat1, double lng1, double lat2, double lng2) {
|
||||
double radLat1 = lat1 * Math.PI / 180.0;
|
||||
double radLat2 = lat2 * Math.PI / 180.0;
|
||||
double a = radLat1 - radLat2;
|
||||
double b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
|
||||
double distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
|
||||
+ Math.cos(radLat1) * Math.cos(radLat2)
|
||||
* Math.pow(Math.sin(b / 2), 2)));
|
||||
distance = distance * 6378.137;
|
||||
distance = Math.round(distance * 10000d) / 10000d;
|
||||
return distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的乘法运算
|
||||
*
|
||||
* 和 hutool {@link NumberUtil#mul(BigDecimal...)} 的差别是,如果存在 null,则返回 null
|
||||
*
|
||||
* @param values 多个被乘值
|
||||
* @return 积
|
||||
*/
|
||||
public static BigDecimal mul(BigDecimal... values) {
|
||||
for (BigDecimal value : values) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return NumberUtil.mul(values);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.njcn.bpm.enums.BpmResponseEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.EnumUtils;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author hongawen
|
||||
* @version 1.0.0
|
||||
* @date 2021年12月20日 10:03
|
||||
*/
|
||||
public class ProcessEnumUtil {
|
||||
|
||||
/**
|
||||
* 获取HarmonicResponseEnum实例
|
||||
*/
|
||||
public static BpmResponseEnum getHarmonicEnumResponseEnumByMessage(@NotNull Object value) {
|
||||
BpmResponseEnum harmonicResponseEnum;
|
||||
try {
|
||||
String message = value.toString();
|
||||
if(message.indexOf(StrUtil.C_COMMA)>0){
|
||||
value = message.substring(message.indexOf(StrUtil.C_COMMA)+1);
|
||||
}
|
||||
harmonicResponseEnum = EnumUtils.valueOf(BpmResponseEnum.class, value, BpmResponseEnum.class.getMethod(BusinessException.GET_MESSAGE_METHOD));
|
||||
return Objects.isNull(harmonicResponseEnum) ? BpmResponseEnum.BPM_COMMON_ERROR : harmonicResponseEnum;
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new BusinessException(CommonResponseEnum.INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
public static Enum<?> getExceptionEnum(HttpResult<Object> result){
|
||||
//如果返回错误,且为内部错误,则直接抛出异常
|
||||
CommonResponseEnum commonResponseEnum = EnumUtils.getCommonResponseEnumByCode(result.getCode());
|
||||
if (commonResponseEnum == CommonResponseEnum.DEVICE_RESPONSE_ENUM) {
|
||||
return getHarmonicEnumResponseEnumByMessage(result.getMessage());
|
||||
}
|
||||
return commonResponseEnum;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 校验工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class ValidationUtils {
|
||||
|
||||
private static final Pattern PATTERN_MOBILE = Pattern.compile("^(?:(?:\\+|00)86)?1(?:(?:3[\\d])|(?:4[0,1,4-9])|(?:5[0-3,5-9])|(?:6[2,5-7])|(?:7[0-8])|(?:8[\\d])|(?:9[0-3,5-9]))\\d{8}$");
|
||||
|
||||
private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
|
||||
|
||||
private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*");
|
||||
|
||||
public static boolean isMobile(String mobile) {
|
||||
return StringUtils.hasText(mobile)
|
||||
&& PATTERN_MOBILE.matcher(mobile).matches();
|
||||
}
|
||||
|
||||
public static boolean isURL(String url) {
|
||||
return StringUtils.hasText(url)
|
||||
&& PATTERN_URL.matcher(url).matches();
|
||||
}
|
||||
|
||||
public static boolean isXmlNCName(String str) {
|
||||
return StringUtils.hasText(str)
|
||||
&& PATTERN_XML_NCNAME.matcher(str).matches();
|
||||
}
|
||||
|
||||
public static void validate(Object object, Class<?>... groups) {
|
||||
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||
Assert.notNull(validator);
|
||||
validate(validator, object, groups);
|
||||
}
|
||||
|
||||
public static void validate(Validator validator, Object object, Class<?>... groups) {
|
||||
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
|
||||
if (CollUtil.isNotEmpty(constraintViolations)) {
|
||||
throw new ConstraintViolationException(constraintViolations);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
102
pqs-bpm/bpm-boot/pom.xml
Normal file
102
pqs-bpm/bpm-boot/pom.xml
Normal file
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>pqs-bpm</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>bpm-boot</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- 每个插件都要引入自己的对外接口 -->
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>common-web</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>bpm-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>user-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>bpmboot</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<configuration>
|
||||
<compilerArgument>-Xlint:unchecked</compilerArgument>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.spotify</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build-image</id>
|
||||
<phase>${docker.operate}</phase>
|
||||
<goals>
|
||||
<goal>build</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<!--<serverId>36dockerHarbor</serverId>-->
|
||||
<registryUrl>http://${docker.repostory}</registryUrl>
|
||||
<!-- 镜像名称 -->
|
||||
<imageName>${docker.repostory}/${docker.registry.name}/${project.artifactId}</imageName>
|
||||
<imageTags>
|
||||
<imageTag>latest</imageTag>
|
||||
</imageTags>
|
||||
<dockerHost>${docker.url}</dockerHost>
|
||||
<dockerDirectory>${basedir}/</dockerDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<targetPath>/ROOT</targetPath>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<include>${project.build.finalName}.jar</include>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.njcn.bpm;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
|
||||
/**
|
||||
* pqs
|
||||
*
|
||||
* @author cdf
|
||||
* @date 2022/11/10
|
||||
*/
|
||||
@Slf4j
|
||||
@DependsOn("proxyMapperRegister")
|
||||
@MapperScan("com.njcn.**.mapper")
|
||||
@EnableFeignClients(basePackages = "com.njcn")
|
||||
@SpringBootApplication(scanBasePackages = "com.njcn")
|
||||
public class BpmApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(BpmApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.njcn.bpm.behavior;
|
||||
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateInvoker;
|
||||
import lombok.Setter;
|
||||
import org.flowable.bpmn.model.Activity;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
|
||||
import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory;
|
||||
|
||||
/**
|
||||
* 自定义的 ActivityBehaviorFactory 实现类,目的如下:
|
||||
* 1. 自定义 {@link #createUserTaskActivityBehavior(UserTask)}:实现自定义的流程任务的 assignee 负责人的分配
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Setter
|
||||
public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory {
|
||||
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
@Override
|
||||
public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) {
|
||||
BpmUserTaskActivityBehavior bpmUserTaskActivityBehavior = new BpmUserTaskActivityBehavior(userTask);
|
||||
bpmUserTaskActivityBehavior.setTaskCandidateInvoker(taskCandidateInvoker);
|
||||
return bpmUserTaskActivityBehavior;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParallelMultiInstanceBehavior createParallelMultiInstanceBehavior(Activity activity,
|
||||
AbstractBpmnActivityBehavior behavior) {
|
||||
BpmParallelMultiInstanceBehavior bpmParallelMultiInstanceBehavior = new BpmParallelMultiInstanceBehavior(activity, behavior);
|
||||
bpmParallelMultiInstanceBehavior.setTaskCandidateInvoker(taskCandidateInvoker);
|
||||
return bpmParallelMultiInstanceBehavior;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SequentialMultiInstanceBehavior createSequentialMultiInstanceBehavior(Activity activity,
|
||||
AbstractBpmnActivityBehavior behavior) {
|
||||
BpmSequentialMultiInstanceBehavior bpmSequentialMultiInstanceBehavior = new BpmSequentialMultiInstanceBehavior(activity, behavior);
|
||||
bpmSequentialMultiInstanceBehavior.setTaskCandidateInvoker(taskCandidateInvoker);
|
||||
return bpmSequentialMultiInstanceBehavior;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.njcn.bpm.behavior;
|
||||
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateInvoker;
|
||||
import com.njcn.bpm.utils.FlowableUtils;
|
||||
import lombok.Setter;
|
||||
import org.flowable.bpmn.model.Activity;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 自定义的【并行】的【多个】流程任务的 assignee 负责人的分配
|
||||
* 第一步,基于分配规则,计算出分配任务的【多个】候选人们。
|
||||
* 第二步,将【多个】任务候选人们,设置到 DelegateExecution 的 collectionVariable 变量中,以便 BpmUserTaskActivityBehavior 使用它
|
||||
*
|
||||
* @author kemengkai
|
||||
* @since 2022-04-21 16:57
|
||||
*/
|
||||
@Setter
|
||||
public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior {
|
||||
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
public BpmParallelMultiInstanceBehavior(Activity activity,
|
||||
AbstractBpmnActivityBehavior innerActivityBehavior) {
|
||||
super(activity, innerActivityBehavior);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写该方法,主要实现两个功能:
|
||||
* 1. 忽略原有的 collectionVariable、collectionElementVariable 表达式,而是采用自己定义的
|
||||
* 2. 获得任务的处理人,并设置到 collectionVariable 中,用于 BpmUserTaskActivityBehavior 从中可以获取任务的处理人
|
||||
*
|
||||
* 注意,多个任务实例,每个任务实例对应一个处理人,所以返回的数量就是任务处理人的数量
|
||||
*
|
||||
* @param execution 执行任务
|
||||
* @return 数量
|
||||
*/
|
||||
@Override
|
||||
protected int resolveNrOfInstances(DelegateExecution execution) {
|
||||
// 第一步,设置 collectionVariable 和 CollectionVariable
|
||||
// 从 execution.getVariable() 读取所有任务处理人的 key
|
||||
super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的
|
||||
super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
|
||||
// 从 execution.getVariable() 读取当前所有任务处理的人的 key
|
||||
super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
|
||||
|
||||
// 第二步,获取任务的所有处理人
|
||||
List<String> assigneeUserIds = taskCandidateInvoker.calculateUsers(execution);
|
||||
execution.setVariable(super.collectionVariable, assigneeUserIds);
|
||||
return assigneeUserIds.size();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.njcn.bpm.behavior;
|
||||
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateInvoker;
|
||||
import com.njcn.bpm.utils.FlowableUtils;
|
||||
import lombok.Setter;
|
||||
import org.flowable.bpmn.model.Activity;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 自定义的【串行】的【多个】流程任务的 assignee 负责人的分配
|
||||
*
|
||||
* 本质上,实现和 {@link BpmParallelMultiInstanceBehavior} 一样,只是继承的类不一样
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Setter
|
||||
public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceBehavior {
|
||||
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
public BpmSequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) {
|
||||
super(activity, innerActivityBehavior);
|
||||
}
|
||||
|
||||
/**
|
||||
* 逻辑和 {@link BpmParallelMultiInstanceBehavior#resolveNrOfInstances(DelegateExecution)} 类似
|
||||
*
|
||||
* 差异的点:是在【第二步】的时候,需要返回 LinkedHashSet 集合!因为它需要有序!
|
||||
*/
|
||||
@Override
|
||||
protected int resolveNrOfInstances(DelegateExecution execution) {
|
||||
// 第一步,设置 collectionVariable 和 CollectionVariable
|
||||
// 从 execution.getVariable() 读取所有任务处理人的 key
|
||||
super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的
|
||||
super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
|
||||
// 从 execution.getVariable() 读取当前所有任务处理的人的 key
|
||||
super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
|
||||
|
||||
// 第二步,获取任务的所有处理人
|
||||
List<String> assigneeUserIds = new ArrayList<>(taskCandidateInvoker.calculateUsers(execution)); // 保证有序!!!
|
||||
execution.setVariable(super.collectionVariable, assigneeUserIds);
|
||||
return assigneeUserIds.size();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.njcn.bpm.behavior;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateInvoker;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.common.engine.impl.el.ExpressionManager;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
|
||||
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
|
||||
import org.flowable.engine.impl.util.TaskHelper;
|
||||
import org.flowable.task.service.TaskService;
|
||||
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 自定义的【单个】流程任务的 assignee 负责人的分配
|
||||
* 第一步,基于分配规则,计算出分配任务的【单个】候选人。如果找不到,则直接报业务异常,不继续执行后续的流程;
|
||||
* 第二步,随机选择一个候选人,则选择作为 assignee 负责人。
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
|
||||
|
||||
@Setter
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
public BpmUserTaskActivityBehavior(UserTask userTask) {
|
||||
super(userTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleAssignments(TaskService taskService, String assignee, String owner,
|
||||
List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager,
|
||||
DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) {
|
||||
// 第一步,获得任务的候选用户
|
||||
String assigneeUserId = calculateTaskCandidateUsers(execution);
|
||||
Assert.notNull(assigneeUserId, "任务处理人不能为空");
|
||||
// 第二步,设置作为负责人
|
||||
TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId));
|
||||
}
|
||||
|
||||
private String calculateTaskCandidateUsers(DelegateExecution execution) {
|
||||
// 情况一,如果是多实例的任务,例如说会签、或签等情况,则从 Variable 中获取。
|
||||
// 顺序审批可见 BpmSequentialMultiInstanceBehavior,并发审批可见 BpmSequentialMultiInstanceBehavior
|
||||
if (super.multiInstanceActivityBehavior != null) {
|
||||
return execution.getVariable(super.multiInstanceActivityBehavior.getCollectionElementVariable(), String.class);
|
||||
}
|
||||
|
||||
// 情况二,如果非多实例的任务,则计算任务处理人
|
||||
// 第一步,先计算可处理该任务的处理人们
|
||||
List<String> candidateUserIds = taskCandidateInvoker.calculateUsers(execution);
|
||||
// 第二步,后随机选择一个任务的处理人
|
||||
// 疑问:为什么一定要选择一个任务处理人?
|
||||
// 解答:项目对 bpm 的任务是责任到人,所以每个任务有且仅有一个处理人。
|
||||
// 如果希望一个任务可以同时被多个人处理,可以考虑使用 BpmParallelMultiInstanceBehavior 实现的会签 or 或签。
|
||||
int index = RandomUtil.randomInt(candidateUserIds.size());
|
||||
return CollUtil.get(candidateUserIds, index);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.njcn.bpm.config;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import com.njcn.bpm.behavior.BpmActivityBehaviorFactory;
|
||||
import com.njcn.bpm.event.BpmProcessInstanceEventPublisher;
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateInvoker;
|
||||
import com.njcn.bpm.strategy.IBpmTaskCandidateStrategy;
|
||||
import com.njcn.user.api.UserFeignClient;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
|
||||
import org.flowable.spring.SpringProcessEngineConfiguration;
|
||||
import org.flowable.spring.boot.EngineConfigurationConfigurer;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.AsyncListenableTaskExecutor;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BPM 模块的 Flowable 配置类
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class BpmFlowableConfiguration {
|
||||
|
||||
/**
|
||||
* 参考 {@link org.flowable.spring.boot.FlowableJobConfiguration} 类,创建对应的 AsyncListenableTaskExecutor Bean
|
||||
*
|
||||
* 如果不创建,会导致项目启动时,Flowable 报错的问题
|
||||
*/
|
||||
@Bean(name = "applicationTaskExecutor")
|
||||
@ConditionalOnMissingBean(name = "applicationTaskExecutor")
|
||||
public AsyncListenableTaskExecutor taskExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(8);
|
||||
executor.setMaxPoolSize(8);
|
||||
executor.setQueueCapacity(100);
|
||||
executor.setThreadNamePrefix("flowable-task-Executor-");
|
||||
executor.setAwaitTerminationSeconds(30);
|
||||
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||
executor.setAllowCoreThreadTimeOut(true);
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* BPM 模块的 ProcessEngineConfigurationConfigurer 实现类:
|
||||
*
|
||||
* 1. 设置各种监听器
|
||||
* 2. 设置自定义的 ActivityBehaviorFactory 实现
|
||||
*/
|
||||
@Bean
|
||||
public EngineConfigurationConfigurer<SpringProcessEngineConfiguration> bpmProcessEngineConfigurationConfigurer(
|
||||
ObjectProvider<FlowableEventListener> listeners,
|
||||
BpmActivityBehaviorFactory bpmActivityBehaviorFactory) {
|
||||
return configuration -> {
|
||||
// 注册监听器,例如说 BpmActivityEventListener
|
||||
configuration.setEventListeners(ListUtil.toList(listeners.iterator()));
|
||||
// 设置 ActivityBehaviorFactory 实现类,用于流程任务的审核人的自定义
|
||||
configuration.setActivityBehaviorFactory(bpmActivityBehaviorFactory);
|
||||
};
|
||||
}
|
||||
|
||||
// =========== 审批人相关的 Bean ==========
|
||||
|
||||
@Bean
|
||||
public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskCandidateInvoker bpmTaskCandidateInvoker) {
|
||||
BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory();
|
||||
bpmActivityBehaviorFactory.setTaskCandidateInvoker(bpmTaskCandidateInvoker);
|
||||
return bpmActivityBehaviorFactory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") // adminUserApi 可以注入成功
|
||||
public BpmTaskCandidateInvoker bpmTaskCandidateInvoker(List<IBpmTaskCandidateStrategy> strategyList,
|
||||
UserFeignClient adminUserApi) {
|
||||
return new BpmTaskCandidateInvoker(strategyList, adminUserApi);
|
||||
}
|
||||
|
||||
// =========== 自己拓展的 Bean ==========
|
||||
|
||||
@Bean
|
||||
public BpmProcessInstanceEventPublisher processInstanceEventPublisher(ApplicationEventPublisher publisher) {
|
||||
return new BpmProcessInstanceEventPublisher(publisher);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.njcn.bpm.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.param.BpmCategoryParam;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.vo.BpmCategoryVO;
|
||||
import com.njcn.bpm.service.IBpmCategoryService;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.HttpResultUtil;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/bpm/category")
|
||||
@Validated
|
||||
@Slf4j
|
||||
@Api(tags = "流程分类控制器")
|
||||
@RequiredArgsConstructor
|
||||
public class BpmCategoryController extends BaseController {
|
||||
|
||||
private final IBpmCategoryService categoryService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
||||
@PostMapping("/add")
|
||||
@ApiOperation("创建流程分类")
|
||||
@ApiImplicitParam(name = "bpmCategoryParam", value = "流程分类数据", required = true)
|
||||
public HttpResult<String> add(@Valid @RequestBody BpmCategoryParam bpmCategoryParam) {
|
||||
String methodDescribe = getMethodDescribe("add");
|
||||
String categoryId = categoryService.createCategory(bpmCategoryParam);
|
||||
if (StrUtil.isNotBlank(categoryId)) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, categoryId, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@PostMapping("/update")
|
||||
@ApiOperation("更新流程分类")
|
||||
@ApiImplicitParam(name = "updateParam", value = "流程分类数据", required = true)
|
||||
public HttpResult<Object> update(@RequestBody @Validated BpmCategoryParam.BpmCategoryUpdateParam updateParam) {
|
||||
String methodDescribe = getMethodDescribe("update");
|
||||
categoryService.updateCategory(updateParam);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@PostMapping("/delete")
|
||||
@ApiOperation("删除流程分类")
|
||||
@ApiImplicitParam(name = "ids", value = "流程分类索引", required = true, dataTypeClass = List.class)
|
||||
public HttpResult<Object> delete(@RequestBody List<String> ids) {
|
||||
String methodDescribe = getMethodDescribe("delete");
|
||||
LogUtil.njcnDebug(log, "{},流程分类ID数据为:{}", methodDescribe, String.join(StrUtil.COMMA, ids));
|
||||
categoryService.deleteCategory(ids);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
@GetMapping("/getById")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@Operation(summary = "获得流程分类")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
public HttpResult<BpmCategory> getById(String id) {
|
||||
String methodDescribe = getMethodDescribe("getById");
|
||||
BpmCategory bpmCategory = categoryService.getById(id);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, bpmCategory, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@PostMapping("/list")
|
||||
@ApiOperation("查询流程分类数据")
|
||||
@ApiImplicitParam(name = "bpmCategoryQueryParam", value = "查询参数", required = true)
|
||||
public HttpResult<Page<BpmCategoryVO>> list(@RequestBody BpmCategoryParam.BpmCategoryQueryParam bpmCategoryQueryParam) {
|
||||
String methodDescribe = getMethodDescribe("list");
|
||||
LogUtil.njcnDebug(log, "{},查询流程分类数据为:{}", methodDescribe, bpmCategoryQueryParam);
|
||||
Page<BpmCategoryVO> result = categoryService.getCategoryPage(bpmCategoryQueryParam);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/simpleList")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@Operation(summary = "获得动态表单的精简列表", description = "用于表单下拉框")
|
||||
public HttpResult<List<BpmCategory>> getCategorySimpleList() {
|
||||
String methodDescribe = getMethodDescribe("getCategorySimpleList");
|
||||
List<BpmCategory> list = categoryService.getCategoryList();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.njcn.bpm.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.param.BpmFormParam;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.vo.BpmFormVO;
|
||||
import com.njcn.bpm.service.IBpmFormService;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.HttpResultUtil;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/bpm/form")
|
||||
@Validated
|
||||
@Slf4j
|
||||
@Api(tags = "流程表单控制器")
|
||||
@RequiredArgsConstructor
|
||||
public class BpmFormController extends BaseController {
|
||||
|
||||
|
||||
private final IBpmFormService formService;
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@PostMapping("/list")
|
||||
@ApiOperation("查询流程表单数据")
|
||||
@ApiImplicitParam(name = "bpmFormQueryParam", value = "查询参数", required = true)
|
||||
public HttpResult<Page<BpmFormVO>> list(@RequestBody BpmFormParam.BpmFormQueryParam bpmFormQueryParam) {
|
||||
String methodDescribe = getMethodDescribe("list");
|
||||
LogUtil.njcnDebug(log, "{},查询流程表单数据为:{}", methodDescribe, bpmFormQueryParam);
|
||||
Page<BpmFormVO> result = formService.getFormPage(bpmFormQueryParam);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
||||
@PostMapping("/add")
|
||||
@ApiOperation("新增流程表单")
|
||||
@ApiImplicitParam(name = "bpmFormParam", value = "流程表单数据", required = true)
|
||||
public HttpResult<String> add(@Valid @RequestBody BpmFormParam bpmFormParam) {
|
||||
String methodDescribe = getMethodDescribe("add");
|
||||
String wfFormId = formService.createForm(bpmFormParam);
|
||||
if (StrUtil.isNotBlank(wfFormId)) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, wfFormId, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@PostMapping("/update")
|
||||
@ApiOperation("更新流程表单")
|
||||
@ApiImplicitParam(name = "updateParam", value = "流程表单数据", required = true)
|
||||
public HttpResult<Object> update(@RequestBody @Validated BpmFormParam.BpmFormUpdateParam updateParam) {
|
||||
String methodDescribe = getMethodDescribe("update");
|
||||
formService.updateForm(updateParam);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@PostMapping("/delete")
|
||||
@ApiOperation("删除流程表单")
|
||||
@ApiImplicitParam(name = "ids", value = "流程表单索引", required = true, dataTypeClass = List.class)
|
||||
public HttpResult<Object> delete(@RequestBody List<String> ids) {
|
||||
String methodDescribe = getMethodDescribe("delete");
|
||||
LogUtil.njcnDebug(log, "{},流程表单ID数据为:{}", methodDescribe, String.join(StrUtil.COMMA, ids));
|
||||
formService.deleteForm(ids);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@GetMapping("/getById")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@Operation(summary = "获得动态表单")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
public HttpResult<BpmForm> getById(String id) {
|
||||
String methodDescribe = getMethodDescribe("getById");
|
||||
BpmForm form = formService.getById(id);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, form, methodDescribe);
|
||||
}
|
||||
|
||||
@GetMapping("/simpleList")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@Operation(summary = "获得动态表单的精简列表", description = "用于表单下拉框")
|
||||
public HttpResult<List<BpmForm>> getFormSimpleList() {
|
||||
String methodDescribe = getMethodDescribe("getFormSimpleList");
|
||||
List<BpmForm> list = formService.getFormList();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package com.njcn.bpm.controller;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO;
|
||||
import com.njcn.bpm.pojo.param.BpmCategoryParam;
|
||||
import com.njcn.bpm.pojo.param.BpmModelParam;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.vo.BpmModelRespVO;
|
||||
import com.njcn.bpm.service.IBpmCategoryService;
|
||||
import com.njcn.bpm.service.IBpmModelService;
|
||||
import com.njcn.bpm.service.IBpmFormService;
|
||||
import com.njcn.bpm.service.IBpmProcessDefinitionService;
|
||||
import com.njcn.bpm.utils.BpmModelConvert;
|
||||
import com.njcn.bpm.utils.CollectionUtils;
|
||||
import com.njcn.bpm.utils.JsonUtils;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.HttpResultUtil;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.Model;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.njcn.bpm.pojo.dto.CommonResult.success;
|
||||
import static com.njcn.bpm.utils.CollectionUtils.convertMap;
|
||||
import static com.njcn.bpm.utils.CollectionUtils.convertSet;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/bpm/model")
|
||||
@Validated
|
||||
@Slf4j
|
||||
@Api(tags = "流程模型控制器")
|
||||
@RequiredArgsConstructor
|
||||
public class BpmModelController extends BaseController {
|
||||
|
||||
private final IBpmModelService modelService;
|
||||
|
||||
private final IBpmFormService formService;
|
||||
|
||||
private final IBpmCategoryService categoryService;
|
||||
|
||||
private final IBpmProcessDefinitionService processDefinitionService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@PostMapping("/list")
|
||||
@ApiOperation("获得模型分页")
|
||||
@ApiImplicitParam(name = "bpmModelQueryParam", value = "查询参数", required = true)
|
||||
public HttpResult<Page<BpmModelRespVO>> getModelPage(@RequestBody BpmModelParam.BpmModelQueryParam bpmModelQueryParam) {
|
||||
String methodDescribe = getMethodDescribe("list");
|
||||
LogUtil.njcnDebug(log, "{},查询流程表单数据为:{}", methodDescribe, bpmModelQueryParam);
|
||||
Page<Model> pageResult = modelService.getModelPage(bpmModelQueryParam);
|
||||
if (CollUtil.isEmpty(pageResult.getRecords())) {
|
||||
Page<BpmModelRespVO> result = new Page<>();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
// 拼接数据
|
||||
// 获得 Form 表单
|
||||
Set<String> formIds = convertSet(pageResult.getRecords(), model -> {
|
||||
BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
|
||||
return metaInfo != null ? metaInfo.getFormId() : null;
|
||||
});
|
||||
Map<String, BpmForm> formMap = formService.getFormMap(formIds);
|
||||
// 获得 Category Map
|
||||
Map<String, BpmCategory> categoryMap = categoryService.getCategoryMap(
|
||||
convertSet(pageResult.getRecords(), Model::getCategory));
|
||||
// 获得 Deployment Map
|
||||
Set<String> deploymentIds = new HashSet<>();
|
||||
pageResult.getRecords().forEach(model -> CollectionUtils.addIfNotNull(deploymentIds, model.getDeploymentId()));
|
||||
|
||||
Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(deploymentIds);
|
||||
// 获得 ProcessDefinition Map
|
||||
List<ProcessDefinition> processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds(deploymentIds);
|
||||
Map<String, ProcessDefinition> processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmModelConvert.INSTANCE.buildModelPage(pageResult, formMap, categoryMap, deploymentMap, processDefinitionMap), methodDescribe);
|
||||
}
|
||||
|
||||
@GetMapping("/getById")
|
||||
@Operation(summary = "获得模型")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
public HttpResult<BpmModelRespVO> getById(String id) {
|
||||
Model model = modelService.getModel(id);
|
||||
String methodDescribe = getMethodDescribe("getById");
|
||||
if (model == null) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
byte[] bpmnBytes = modelService.getModelBpmnXML(id);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmModelConvert.INSTANCE.buildModel(model, bpmnBytes), methodDescribe);
|
||||
}
|
||||
|
||||
@Operation(summary = "新建模型")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
||||
@PostMapping("/add")
|
||||
@ApiOperation("新建模型")
|
||||
@ApiImplicitParam(name = "createRetVO", value = "模型数据", required = true)
|
||||
public HttpResult<String> createModel(@Validated @RequestBody BpmModelParam bpmModelParam) {
|
||||
String methodDescribe = getMethodDescribe("createModel");
|
||||
String modelId = modelService.createModel(bpmModelParam, null);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, modelId, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@PostMapping("/update")
|
||||
@ApiOperation("修改模型")
|
||||
@ApiImplicitParam(name = "updateParam", value = "流程分类数据", required = true)
|
||||
public HttpResult<Object> update( @Validated @RequestBody BpmModelParam.BpmModelUpdateParam updateParam) {
|
||||
String methodDescribe = getMethodDescribe("update");
|
||||
modelService.updateModel(updateParam);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
// @PostMapping("/import")
|
||||
// @Operation(summary = "导入模型")
|
||||
// @PreAuthorize("@ss.hasPermission('bpm:model:import')")
|
||||
// public CommonResult<String> importModel(@Valid BpmModeImportReqVO importReqVO) throws IOException {
|
||||
// BpmModelCreateReqVO createReqVO = BeanUtils.toBean(importReqVO, BpmModelCreateReqVO.class);
|
||||
// // 读取文件
|
||||
// String bpmnXml = IoUtils.readUtf8(importReqVO.getBpmnFile().getInputStream(), false);
|
||||
// return success(modelService.createModel(createReqVO, bpmnXml));
|
||||
// }
|
||||
|
||||
|
||||
@PostMapping("/deploy")
|
||||
@Operation(summary = "部署模型")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
public HttpResult<Boolean> deployModel(String id) {
|
||||
String methodDescribe = getMethodDescribe("deployModel");
|
||||
modelService.deployModel(id);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
//
|
||||
// @PutMapping("/update-state")
|
||||
// @Operation(summary = "修改模型的状态", description = "实际更新的部署的流程定义的状态")
|
||||
// @PreAuthorize("@ss.hasPermission('bpm:model:update')")
|
||||
// public CommonResult<Boolean> updateModelState(@Valid @RequestBody BpmModelUpdateStateReqVO reqVO) {
|
||||
// modelService.updateModelState(reqVO.getId(), reqVO.getState());
|
||||
// return success(true);
|
||||
// }
|
||||
//
|
||||
// @DeleteMapping("/delete")
|
||||
// @Operation(summary = "删除模型")
|
||||
// @Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
// @PreAuthorize("@ss.hasPermission('bpm:model:delete')")
|
||||
// public CommonResult<Boolean> deleteModel(@RequestParam("id") String id) {
|
||||
// modelService.deleteModel(id);
|
||||
// return success(true);
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.njcn.bpm.controller;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.param.BpmProcessDefinitionInfoParam;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO;
|
||||
import com.njcn.bpm.service.IBpmCategoryService;
|
||||
import com.njcn.bpm.service.IBpmFormService;
|
||||
import com.njcn.bpm.service.IBpmProcessDefinitionService;
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateStartUserSelectStrategy;
|
||||
import com.njcn.bpm.utils.BpmProcessDefinitionConvert;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.HttpResultUtil;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.njcn.bpm.utils.CollectionUtils.convertSet;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/bpm/processDefinition")
|
||||
@Validated
|
||||
@Slf4j
|
||||
@Api(tags = "管理后台 - 流程定义")
|
||||
@RequiredArgsConstructor
|
||||
public class BpmProcessDefinitionController extends BaseController {
|
||||
|
||||
private final IBpmProcessDefinitionService processDefinitionService;
|
||||
|
||||
private final IBpmFormService formService;
|
||||
|
||||
private final IBpmCategoryService categoryService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@PostMapping("/page")
|
||||
@ApiOperation("获得流程定义分页")
|
||||
@ApiImplicitParam(name = "bpmProcessDefinitionInfoQueryParam", value = "查询参数", required = true)
|
||||
public HttpResult<Page<BpmProcessDefinitionInfoVO>> getProcessDefinitionPage(
|
||||
BpmProcessDefinitionInfoParam.BpmProcessDefinitionInfoQueryParam bpmProcessDefinitionInfoQueryParam) {
|
||||
String methodDescribe = getMethodDescribe("getProcessDefinitionPage");
|
||||
Page<ProcessDefinition> pageResult = processDefinitionService.getProcessDefinitionPage(bpmProcessDefinitionInfoQueryParam);
|
||||
if (CollUtil.isEmpty(pageResult.getRecords())) {
|
||||
Page<BpmProcessDefinitionInfoVO> result = new Page<>();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
// 获得 Category Map
|
||||
Map<String, BpmCategory> categoryMap = categoryService.getCategoryMap(
|
||||
convertSet(pageResult.getRecords(), ProcessDefinition::getCategory));
|
||||
// 获得 Deployment Map
|
||||
Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(
|
||||
convertSet(pageResult.getRecords(), ProcessDefinition::getDeploymentId));
|
||||
// 获得 BpmProcessDefinitionInfoDO Map
|
||||
Map<String, BpmProcessDefinitionInfo> processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap(
|
||||
convertSet(pageResult.getRecords(), ProcessDefinition::getId));
|
||||
// 获得 Form Map
|
||||
Map<String, BpmForm> formMap = formService.getFormMap(
|
||||
convertSet(processDefinitionMap.values(), BpmProcessDefinitionInfo::getFormId));
|
||||
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionPage(
|
||||
pageResult, deploymentMap, processDefinitionMap, formMap, categoryMap), methodDescribe);
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得流程定义列表")
|
||||
@Parameter(name = "suspensionState", description = "挂起状态", required = true) // 参见 Flowable SuspensionState 枚举
|
||||
public HttpResult<List<BpmProcessDefinitionInfoVO>> getProcessDefinitionList(Integer suspensionState) {
|
||||
String methodDescribe = getMethodDescribe("getProcessDefinitionList");
|
||||
List<ProcessDefinition> list = processDefinitionService.getProcessDefinitionListBySuspensionState(suspensionState);
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, new ArrayList<>(), methodDescribe);
|
||||
}
|
||||
|
||||
// 获得 BpmProcessDefinitionInfoDO Map
|
||||
Map<String, BpmProcessDefinitionInfo> processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap(
|
||||
convertSet(list, ProcessDefinition::getId));
|
||||
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionList(
|
||||
list, null, processDefinitionMap, null, null), methodDescribe);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得流程定义")
|
||||
@Parameter(name = "id", description = "流程编号", required = true, example = "1024")
|
||||
@Parameter(name = "key", description = "流程定义标识", required = true, example = "1024")
|
||||
public HttpResult<BpmProcessDefinitionInfoVO> getProcessDefinition(
|
||||
@RequestParam(value = "id", required = false) String id,
|
||||
@RequestParam(value = "key", required = false) String key) {
|
||||
String methodDescribe = getMethodDescribe("getProcessDefinition");
|
||||
ProcessDefinition processDefinition = id != null ? processDefinitionService.getProcessDefinition(id)
|
||||
: processDefinitionService.getActiveProcessDefinition(key);
|
||||
if (processDefinition == null) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());
|
||||
List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(
|
||||
processDefinition, null, null, null, null, bpmnModel, userTaskList), methodDescribe);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.njcn.bpm.event;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Validated
|
||||
public class BpmProcessInstanceEventPublisher {
|
||||
|
||||
private final ApplicationEventPublisher publisher;
|
||||
|
||||
public void sendProcessInstanceResultEvent(@Valid BpmProcessInstanceStatusEvent event) {
|
||||
publisher.publishEvent(event);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.njcn.bpm.event;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 流程实例的状态(结果)发生变化的 Event
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@SuppressWarnings("ALL")
|
||||
@Data
|
||||
public class BpmProcessInstanceStatusEvent extends ApplicationEvent {
|
||||
|
||||
/**
|
||||
* 流程实例的编号
|
||||
*/
|
||||
@NotNull(message = "流程实例的编号不能为空")
|
||||
private String id;
|
||||
/**
|
||||
* 流程实例的 key
|
||||
*/
|
||||
@NotNull(message = "流程实例的 key 不能为空")
|
||||
private String processDefinitionKey;
|
||||
/**
|
||||
* 流程实例的结果
|
||||
*/
|
||||
@NotNull(message = "流程实例的状态不能为空")
|
||||
private Integer status;
|
||||
/**
|
||||
* 流程实例对应的业务标识
|
||||
* 例如说,请假
|
||||
*/
|
||||
private String businessKey;
|
||||
|
||||
public BpmProcessInstanceStatusEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.njcn.bpm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.vo.BpmCategoryVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
|
||||
/**
|
||||
* BPM 流程分类 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface BpmCategoryMapper extends BaseMapper<BpmCategory> {
|
||||
|
||||
|
||||
Page<BpmCategoryVO> page(@Param("page") Page<Object> objectPage, @Param("ew") QueryWrapper<BpmCategoryVO> categoryVOQueryWrapper);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.njcn.bpm.mapper;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.vo.BpmFormVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 动态表单 Mapper
|
||||
*
|
||||
* @author 风里雾里
|
||||
*/
|
||||
@Mapper
|
||||
public interface BpmFormMapper extends BaseMapper<BpmForm> {
|
||||
Page<BpmFormVO> page(@Param("page")Page<Object> objectPage, @Param("ew") QueryWrapper<BpmFormVO> categoryVOQueryWrapper);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.njcn.bpm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
|
||||
@Mapper
|
||||
public interface BpmProcessDefinitionInfoMapper extends BaseMapper<BpmProcessDefinitionInfo> {
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.bpm.mapper.BpmCategoryMapper">
|
||||
|
||||
<!--获取流程表单分页列表-->
|
||||
<select id="page" resultType="BpmCategoryVO">
|
||||
SELECT
|
||||
bpm_category.id,
|
||||
bpm_category.name,
|
||||
bpm_category.code,
|
||||
bpm_category.status,
|
||||
bpm_category.create_time,
|
||||
bpm_category.remark
|
||||
FROM bpm_category bpm_category
|
||||
WHERE ${ew.sqlSegment}
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.bpm.mapper.BpmFormMapper">
|
||||
|
||||
<!--获取流程表单分页列表-->
|
||||
<select id="page" resultType="BpmFormVO">
|
||||
SELECT
|
||||
bpm_form.id,
|
||||
bpm_form.name,
|
||||
bpm_form.status,
|
||||
bpm_form.create_time,
|
||||
bpm_form.remark
|
||||
FROM bpm_form bpm_form
|
||||
WHERE ${ew.sqlSegment}
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.njcn.bpm.service;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.bpm.pojo.param.BpmCategoryParam;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.vo.BpmCategoryVO;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.njcn.bpm.utils.CollectionUtils.convertMap;
|
||||
|
||||
|
||||
/**
|
||||
* BPM 流程分类 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IBpmCategoryService extends IService<BpmCategory> {
|
||||
|
||||
/**
|
||||
* 创建流程分类
|
||||
*
|
||||
* @return 编号
|
||||
*/
|
||||
String createCategory( BpmCategoryParam bpmCategoryParam);
|
||||
|
||||
/**
|
||||
* 更新流程分类
|
||||
*
|
||||
*/
|
||||
void updateCategory( BpmCategoryParam.BpmCategoryUpdateParam updateParam);
|
||||
|
||||
/**
|
||||
* 删除流程分类
|
||||
*/
|
||||
void deleteCategory(List<String> ids);
|
||||
|
||||
|
||||
/**
|
||||
* 获得流程分类分页
|
||||
*
|
||||
* @return 流程分类分页
|
||||
*/
|
||||
Page<BpmCategoryVO> getCategoryPage(BpmCategoryParam.BpmCategoryQueryParam bpmCategoryQueryParam);
|
||||
|
||||
/**
|
||||
* 获得流程分类 Map,基于指定编码
|
||||
*
|
||||
* @param codes 编号数组
|
||||
* @return 流程分类 Map
|
||||
*/
|
||||
default Map<String, BpmCategory> getCategoryMap(Collection<String> codes) {
|
||||
return convertMap(getCategoryListByCode(codes), BpmCategory::getCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程分类列表,基于指定编码
|
||||
*
|
||||
* @return 流程分类列表
|
||||
*/
|
||||
List<BpmCategory> getCategoryListByCode(Collection<String> codes);
|
||||
|
||||
|
||||
|
||||
List<BpmCategory> getCategoryList();
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.njcn.bpm.service;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.bpm.pojo.param.BpmFormParam;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.vo.BpmFormVO;
|
||||
import com.njcn.bpm.utils.CollectionUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* 动态表单 Service 接口
|
||||
*
|
||||
* @author @风里雾里
|
||||
*/
|
||||
public interface IBpmFormService extends IService<BpmForm> {
|
||||
|
||||
/**
|
||||
* 创建动态表单
|
||||
*
|
||||
* @return 编号
|
||||
*/
|
||||
String createForm(BpmFormParam bpmFormParam);
|
||||
|
||||
/**
|
||||
* 更新动态表单
|
||||
*
|
||||
*/
|
||||
void updateForm(BpmFormParam.BpmFormUpdateParam updateParam);
|
||||
|
||||
/**
|
||||
* 删除动态表单
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteForm(List<String> id);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获得动态表单列表
|
||||
*
|
||||
* @return 动态表单列表
|
||||
*/
|
||||
List<BpmForm> getFormList();
|
||||
|
||||
/**
|
||||
* 获得动态表单列表
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 动态表单列表
|
||||
*/
|
||||
List<BpmForm> getFormList(Collection<String> ids);
|
||||
|
||||
/**
|
||||
* 获得动态表单 Map
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 动态表单 Map
|
||||
*/
|
||||
default Map<String, BpmForm> getFormMap(Collection<String> ids) {
|
||||
return CollectionUtils.convertMap(this.getFormList(ids), BpmForm::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得动态表单分页
|
||||
*
|
||||
* @return 动态表单分页
|
||||
*/
|
||||
Page<BpmFormVO> getFormPage(BpmFormParam.BpmFormQueryParam bpmFormQueryParam);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.njcn.bpm.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.param.BpmModelParam;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.engine.repository.Model;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* Flowable流程模型接口
|
||||
*
|
||||
* @author yunlongn
|
||||
*/
|
||||
public interface IBpmModelService {
|
||||
|
||||
/**
|
||||
* 获得流程模型分页
|
||||
*
|
||||
* @return 流程模型分页
|
||||
*/
|
||||
Page<Model> getModelPage(BpmModelParam.BpmModelQueryParam bpmModelQueryParam);
|
||||
|
||||
/**
|
||||
* 创建流程模型
|
||||
*
|
||||
* @param bpmnXml BPMN XML
|
||||
* @return 创建的流程模型的编号
|
||||
*/
|
||||
String createModel(BpmModelParam bpmModelParam, String bpmnXml);
|
||||
|
||||
/**
|
||||
* 获得流程模块
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 流程模型
|
||||
*/
|
||||
Model getModel(String id);
|
||||
|
||||
/**
|
||||
* 获得流程模型的 BPMN XML
|
||||
*
|
||||
* @param id 编号
|
||||
* @return BPMN XML
|
||||
*/
|
||||
byte[] getModelBpmnXML(String id);
|
||||
|
||||
/**
|
||||
* 修改流程模型
|
||||
*
|
||||
*/
|
||||
void updateModel(BpmModelParam.BpmModelUpdateParam updateParam);
|
||||
|
||||
/**
|
||||
* 将流程模型,部署成一个流程定义
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deployModel(String id);
|
||||
|
||||
/**
|
||||
* 删除模型
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteModel(String id);
|
||||
|
||||
/**
|
||||
* 修改模型的状态,实际更新的部署的流程定义的状态
|
||||
*
|
||||
* @param id 编号
|
||||
* @param state 状态
|
||||
*/
|
||||
void updateModelState(String id, Integer state);
|
||||
|
||||
/**
|
||||
* 获得流程定义编号对应的 BPMN Model
|
||||
*
|
||||
* @param processDefinitionId 流程定义编号
|
||||
* @return BPMN Model
|
||||
*/
|
||||
BpmnModel getBpmnModelByDefinitionId(String processDefinitionId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.njcn.bpm.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO;
|
||||
import com.njcn.bpm.pojo.param.BpmProcessDefinitionInfoParam;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.Model;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.njcn.bpm.utils.CollectionUtils.convertMap;
|
||||
|
||||
|
||||
/**
|
||||
* Flowable流程定义接口
|
||||
*
|
||||
* @author yunlong.li
|
||||
* @author ZJQ
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IBpmProcessDefinitionService extends IService<BpmProcessDefinitionInfo> {
|
||||
|
||||
/**
|
||||
* 获得流程定义分页
|
||||
*
|
||||
* @return 流程定义 Page
|
||||
*/
|
||||
Page<ProcessDefinition> getProcessDefinitionPage(BpmProcessDefinitionInfoParam.BpmProcessDefinitionInfoQueryParam bpmProcessDefinitionInfoQueryParam);
|
||||
|
||||
/**
|
||||
* 获得流程定义列表
|
||||
*
|
||||
* @param suspensionState 中断状态
|
||||
* @return 流程定义列表
|
||||
*/
|
||||
List<ProcessDefinition> getProcessDefinitionListBySuspensionState(Integer suspensionState);
|
||||
|
||||
/**
|
||||
* 基于流程模型,创建流程定义
|
||||
*
|
||||
* @param model 流程模型
|
||||
* @param modelMetaInfo 流程模型元信息
|
||||
* @param bpmnBytes BPMN XML 字节数组
|
||||
* @param form 表单
|
||||
* @return 流程编号
|
||||
*/
|
||||
String createProcessDefinition(Model model, BpmModelMetaInfoRespDTO modelMetaInfo, byte[] bpmnBytes, BpmForm form);
|
||||
|
||||
/**
|
||||
* 更新流程定义状态
|
||||
*
|
||||
* @param id 流程定义的编号
|
||||
* @param state 状态
|
||||
*/
|
||||
void updateProcessDefinitionState(String id, Integer state);
|
||||
|
||||
/**
|
||||
* 获得流程定义对应的 BPMN
|
||||
*
|
||||
* @param id 流程定义编号
|
||||
* @return BPMN
|
||||
*/
|
||||
BpmnModel getProcessDefinitionBpmnModel(String id);
|
||||
|
||||
/**
|
||||
* 获得流程定义的信息
|
||||
*
|
||||
* @param id 流程定义编号
|
||||
* @return 流程定义信息
|
||||
*/
|
||||
BpmProcessDefinitionInfo getProcessDefinitionInfo(String id);
|
||||
|
||||
/**
|
||||
* 获得流程定义的信息 List
|
||||
*
|
||||
* @param ids 流程定义编号数组
|
||||
* @return 流程额定义信息数组
|
||||
*/
|
||||
List<BpmProcessDefinitionInfo> getProcessDefinitionInfoList(Collection<String> ids);
|
||||
|
||||
default Map<String, BpmProcessDefinitionInfo> getProcessDefinitionInfoMap(Set<String> ids) {
|
||||
return convertMap(getProcessDefinitionInfoList(ids), BpmProcessDefinitionInfo::getProcessDefinitionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程定义编号对应的 ProcessDefinition
|
||||
*
|
||||
* @param id 流程定义编号
|
||||
* @return 流程定义
|
||||
*/
|
||||
ProcessDefinition getProcessDefinition(String id);
|
||||
|
||||
/**
|
||||
* 获得 ids 对应的 ProcessDefinition 数组
|
||||
*
|
||||
* @param ids 编号的数组
|
||||
* @return 流程定义的数组
|
||||
*/
|
||||
List<ProcessDefinition> getProcessDefinitionList(Set<String> ids);
|
||||
|
||||
default Map<String, ProcessDefinition> getProcessDefinitionMap(Set<String> ids) {
|
||||
return convertMap(getProcessDefinitionList(ids), ProcessDefinition::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 deploymentId 对应的 ProcessDefinition
|
||||
*
|
||||
* @param deploymentId 部署编号
|
||||
* @return 流程定义
|
||||
*/
|
||||
ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId);
|
||||
|
||||
/**
|
||||
* 获得 deploymentIds 对应的 ProcessDefinition 数组
|
||||
*
|
||||
* @param deploymentIds 部署编号的数组
|
||||
* @return 流程定义的数组
|
||||
*/
|
||||
List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds);
|
||||
|
||||
/**
|
||||
* 获得流程定义标识对应的激活的流程定义
|
||||
*
|
||||
* @param key 流程定义的标识
|
||||
* @return 流程定义
|
||||
*/
|
||||
ProcessDefinition getActiveProcessDefinition(String key);
|
||||
|
||||
/**
|
||||
* 获得 ids 对应的 Deployment Map
|
||||
*
|
||||
* @param ids 部署编号的数组
|
||||
* @return 流程部署 Map
|
||||
*/
|
||||
default Map<String, Deployment> getDeploymentMap(Set<String> ids) {
|
||||
return convertMap(getDeploymentList(ids), Deployment::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 ids 对应的 Deployment 数组
|
||||
*
|
||||
* @param ids 部署编号的数组
|
||||
* @return 流程部署的数组
|
||||
*/
|
||||
List<Deployment> getDeploymentList(Set<String> ids);
|
||||
|
||||
/**
|
||||
* 获得 id 对应的 Deployment
|
||||
*
|
||||
* @param id 部署编号
|
||||
* @return 流程部署
|
||||
*/
|
||||
Deployment getDeployment(String id);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.njcn.bpm.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.bpm.enums.BpmResponseEnum;
|
||||
import com.njcn.bpm.mapper.BpmCategoryMapper;
|
||||
import com.njcn.bpm.pojo.param.BpmCategoryParam;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.vo.BpmCategoryVO;
|
||||
import com.njcn.bpm.pojo.vo.BpmFormVO;
|
||||
import com.njcn.bpm.service.IBpmCategoryService;
|
||||
import com.njcn.bpm.utils.BeanUtils;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BPM 流程分类 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class BpmCategoryServiceImpl extends ServiceImpl<BpmCategoryMapper, BpmCategory> implements IBpmCategoryService {
|
||||
|
||||
|
||||
@Override
|
||||
public String createCategory(BpmCategoryParam bpmCategoryParam) {
|
||||
// 校验唯一
|
||||
checkCategoryName(bpmCategoryParam, false);
|
||||
checkCategoryCode(bpmCategoryParam, false);
|
||||
// 插入
|
||||
BpmCategory category = BeanUtils.toBean(bpmCategoryParam, BpmCategory.class);
|
||||
this.baseMapper.insert(category);
|
||||
return category.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCategory(BpmCategoryParam.BpmCategoryUpdateParam updateParam) {
|
||||
// 校验唯一
|
||||
checkCategoryName(updateParam, true);
|
||||
checkCategoryCode(updateParam, true);
|
||||
// 更新
|
||||
BpmCategory updateObj = BeanUtils.toBean(updateParam, BpmCategory.class);
|
||||
this.baseMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void deleteCategory(List<String> ids) {
|
||||
this.lambdaUpdate().set(BpmCategory::getState, DataStateEnum.DELETED.getCode())
|
||||
.in(BpmCategory::getId, ids)
|
||||
.update();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Page<BpmCategoryVO> getCategoryPage(BpmCategoryParam.BpmCategoryQueryParam bpmCategoryQueryParam) {
|
||||
QueryWrapper<BpmCategoryVO> categoryVOQueryWrapper = new QueryWrapper<>();
|
||||
if (StrUtil.isNotBlank(bpmCategoryQueryParam.getName())) {
|
||||
categoryVOQueryWrapper.like("bpm_category.name", bpmCategoryQueryParam.getName());
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(bpmCategoryQueryParam.getCode())) {
|
||||
categoryVOQueryWrapper.like("bpm_category.name", bpmCategoryQueryParam.getCode());
|
||||
}
|
||||
categoryVOQueryWrapper.eq("bpm_category.state", DataStateEnum.ENABLE.getCode());
|
||||
categoryVOQueryWrapper.orderByDesc("bpm_category.update_time");
|
||||
return this.baseMapper.page(new Page<>(PageFactory.getPageNum(bpmCategoryQueryParam), PageFactory.getPageSize(bpmCategoryQueryParam)), categoryVOQueryWrapper);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BpmCategory> getCategoryListByCode(Collection<String> codes) {
|
||||
if (CollUtil.isEmpty(codes)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
LambdaQueryWrapper<BpmCategory> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.in(BpmCategory::getCode,codes);
|
||||
return this.baseMapper.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BpmCategory> getCategoryList() {
|
||||
LambdaQueryWrapper<BpmCategory> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.select(BpmCategory::getId,BpmCategory::getName)
|
||||
.eq(BpmCategory::getState,DataStateEnum.ENABLE.getCode());
|
||||
return this.baseMapper.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
|
||||
private void checkCategoryName(BpmCategoryParam bpmCategoryParam, boolean isExcludeSelf) {
|
||||
LambdaQueryWrapper<BpmCategory> categoryLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
//判断流程表单的名称
|
||||
categoryLambdaQueryWrapper
|
||||
.eq(BpmCategory::getName, bpmCategoryParam.getName())
|
||||
.eq(BpmCategory::getState, DataStateEnum.ENABLE.getCode());
|
||||
//更新的时候,需排除当前记录
|
||||
if (isExcludeSelf) {
|
||||
if (bpmCategoryParam instanceof BpmCategoryParam.BpmCategoryUpdateParam) {
|
||||
categoryLambdaQueryWrapper.ne(BpmCategory::getId, ((BpmCategoryParam.BpmCategoryUpdateParam) bpmCategoryParam).getId());
|
||||
}
|
||||
}
|
||||
int nameCountByAccount = this.count(categoryLambdaQueryWrapper);
|
||||
//大于等于1个则表示重复
|
||||
if (nameCountByAccount >= 1) {
|
||||
throw new BusinessException(BpmResponseEnum.REPEAT_CATEGORY_NAME_FORM);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCategoryCode(BpmCategoryParam bpmCategoryParam, boolean isExcludeSelf) {
|
||||
LambdaQueryWrapper<BpmCategory> categoryLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
//判断流程表单的名称
|
||||
categoryLambdaQueryWrapper
|
||||
.eq(BpmCategory::getCode, bpmCategoryParam.getCode())
|
||||
.eq(BpmCategory::getState, DataStateEnum.ENABLE.getCode());
|
||||
//更新的时候,需排除当前记录
|
||||
if (isExcludeSelf) {
|
||||
if (bpmCategoryParam instanceof BpmCategoryParam.BpmCategoryUpdateParam) {
|
||||
categoryLambdaQueryWrapper.ne(BpmCategory::getId, ((BpmCategoryParam.BpmCategoryUpdateParam) bpmCategoryParam).getId());
|
||||
}
|
||||
}
|
||||
int nameCountByAccount = this.count(categoryLambdaQueryWrapper);
|
||||
//大于等于1个则表示重复
|
||||
if (nameCountByAccount >= 1) {
|
||||
throw new BusinessException(BpmResponseEnum.REPEAT_CATEGORY_NAME_FORM);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.njcn.bpm.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.bpm.enums.BpmResponseEnum;
|
||||
import com.njcn.bpm.mapper.BpmFormMapper;
|
||||
import com.njcn.bpm.pojo.param.BpmFormParam;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.vo.BpmFormVO;
|
||||
import com.njcn.bpm.service.IBpmFormService;
|
||||
import com.njcn.bpm.utils.BeanUtils;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* 动态表单 Service 实现类
|
||||
*
|
||||
* @author 风里雾里
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class BpmFormServiceImpl extends ServiceImpl<BpmFormMapper, BpmForm> implements IBpmFormService {
|
||||
|
||||
@Override
|
||||
public Page<BpmFormVO> getFormPage(BpmFormParam.BpmFormQueryParam bpmFormQueryParam) {
|
||||
QueryWrapper<BpmFormVO> bpmFormVOQueryWrapper = new QueryWrapper<>();
|
||||
if (StrUtil.isNotBlank(bpmFormQueryParam.getName())) {
|
||||
bpmFormVOQueryWrapper.like("bpm_form.name", bpmFormQueryParam.getName());
|
||||
}
|
||||
bpmFormVOQueryWrapper.eq("bpm_form.state", DataStateEnum.ENABLE.getCode());
|
||||
bpmFormVOQueryWrapper.orderByDesc("bpm_form.update_time");
|
||||
return this.baseMapper.page(new Page<>(PageFactory.getPageNum(bpmFormQueryParam), PageFactory.getPageSize(bpmFormQueryParam)), bpmFormVOQueryWrapper);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String createForm(BpmFormParam bpmFormParam) {
|
||||
// this.validateFields(createReqVO.getFields());
|
||||
checkFormName(bpmFormParam, false);
|
||||
// 插入
|
||||
BpmForm form = BeanUtils.toBean(bpmFormParam, BpmForm.class);
|
||||
this.baseMapper.insert(form);
|
||||
// 返回
|
||||
return form.getId();
|
||||
}
|
||||
|
||||
private void checkFormName(BpmFormParam bpmFormParam, boolean isExcludeSelf) {
|
||||
LambdaQueryWrapper<BpmForm> categoryLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
//判断流程表单的名称
|
||||
categoryLambdaQueryWrapper
|
||||
.eq(BpmForm::getName, bpmFormParam.getName())
|
||||
.eq(BpmForm::getState, DataStateEnum.ENABLE.getCode());
|
||||
//更新的时候,需排除当前记录
|
||||
if (isExcludeSelf) {
|
||||
if (bpmFormParam instanceof BpmFormParam.BpmFormUpdateParam) {
|
||||
categoryLambdaQueryWrapper.ne(BpmForm::getId, ((BpmFormParam.BpmFormUpdateParam) bpmFormParam).getId());
|
||||
}
|
||||
}
|
||||
int nameCountByAccount = this.count(categoryLambdaQueryWrapper);
|
||||
//大于等于1个则表示重复
|
||||
if (nameCountByAccount >= 1) {
|
||||
throw new BusinessException(BpmResponseEnum.REPEAT_NAME_FORM);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateForm(BpmFormParam.BpmFormUpdateParam updateParam) {
|
||||
checkFormName(updateParam, true);
|
||||
BpmForm updateObj = BeanUtils.toBean(updateParam, BpmForm.class);
|
||||
this.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteForm(List<String> ids) {
|
||||
this.lambdaUpdate().set(BpmForm::getState, DataStateEnum.DELETED.getCode())
|
||||
.in(BpmForm::getId, ids)
|
||||
.update();
|
||||
}
|
||||
|
||||
// private void validateFormExists(Long id) {
|
||||
// if (this.baseMapper.selectById(id) == null) {
|
||||
// throw exception(ErrorCodeConstants.FORM_NOT_EXISTS);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public List<BpmForm> getFormList() {
|
||||
LambdaQueryWrapper<BpmForm> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.select(BpmForm::getId,BpmForm::getName)
|
||||
.eq(BpmForm::getState,DataStateEnum.ENABLE.getCode());
|
||||
return this.baseMapper.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BpmForm> getFormList(Collection<String> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return this.baseMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 校验 Field,避免 field 重复
|
||||
*
|
||||
* @param fields field 数组
|
||||
*/
|
||||
private void validateFields(List<String> fields) {
|
||||
if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验
|
||||
return;
|
||||
}
|
||||
// Map<String, String> fieldMap = new HashMap<>(); // key 是 vModel,value 是 label
|
||||
// for (String field : fields) {
|
||||
// BpmFormFieldRespDTO fieldDTO = JsonUtils.parseObject(field, BpmFormFieldRespDTO.class);
|
||||
// Assert.notNull(fieldDTO);
|
||||
// String oldLabel = fieldMap.put(fieldDTO.getVModel(), fieldDTO.getLabel());
|
||||
// // 如果不存在,则直接返回
|
||||
// if (oldLabel == null) {
|
||||
// continue;
|
||||
// }
|
||||
// // 如果存在,则报错
|
||||
// throw exception(ErrorCodeConstants.FORM_FIELD_REPEAT, oldLabel, fieldDTO.getLabel(), fieldDTO.getVModel());
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
package com.njcn.bpm.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.enums.BpmModelFormTypeEnum;
|
||||
import com.njcn.bpm.enums.BpmResponseEnum;
|
||||
import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO;
|
||||
import com.njcn.bpm.pojo.param.BpmModelParam;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.service.IBpmFormService;
|
||||
import com.njcn.bpm.service.IBpmModelService;
|
||||
import com.njcn.bpm.service.IBpmProcessDefinitionService;
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateInvoker;
|
||||
import com.njcn.bpm.utils.*;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.StartEvent;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.common.engine.impl.db.SuspensionState;
|
||||
import org.flowable.engine.ProcessEngineConfiguration;
|
||||
import org.flowable.engine.RepositoryService;
|
||||
import org.flowable.engine.repository.Model;
|
||||
import org.flowable.engine.repository.ModelQuery;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/**
|
||||
* Flowable流程模型实现
|
||||
* 主要进行 Flowable {@link Model} 的维护
|
||||
*
|
||||
* @author yunlongn
|
||||
* @author 芋道源码
|
||||
* @author jason
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class BpmModelServiceImpl implements IBpmModelService {
|
||||
|
||||
@Resource
|
||||
private RepositoryService repositoryService;
|
||||
@Resource
|
||||
private IBpmProcessDefinitionService processDefinitionService;
|
||||
@Resource
|
||||
private IBpmFormService bpmFormService;
|
||||
@Resource
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
@Override
|
||||
public Page<Model> getModelPage(BpmModelParam.BpmModelQueryParam bpmModelQueryParam) {
|
||||
ModelQuery modelQuery = repositoryService.createModelQuery();
|
||||
if (StrUtil.isNotBlank(bpmModelQueryParam.getKey())) {
|
||||
modelQuery.modelKey(bpmModelQueryParam.getKey());
|
||||
}
|
||||
if (StrUtil.isNotBlank(bpmModelQueryParam.getName())) {
|
||||
modelQuery.modelNameLike("%" + bpmModelQueryParam.getName() + "%"); // 模糊匹配
|
||||
}
|
||||
if (StrUtil.isNotBlank(bpmModelQueryParam.getCategory())) {
|
||||
modelQuery.modelCategory(bpmModelQueryParam.getCategory());
|
||||
}
|
||||
// 执行查询
|
||||
long count = modelQuery.count();
|
||||
if (count == 0) {
|
||||
return new Page<>();
|
||||
}
|
||||
int offset = PageFactory.getPageSize(bpmModelQueryParam) * (PageFactory.getPageNum(bpmModelQueryParam) - 1);
|
||||
List<Model> models = modelQuery
|
||||
.modelTenantId(ProcessEngineConfiguration.NO_TENANT_ID)
|
||||
.orderByCreateTime().desc()
|
||||
.listPage(offset, PageFactory.getPageSize(bpmModelQueryParam));
|
||||
|
||||
Page<Model> page = new Page<>();
|
||||
page.setRecords(models);
|
||||
page.setTotal(count);
|
||||
page.setSize(PageFactory.getPageSize(bpmModelQueryParam));
|
||||
page.setCurrent(PageFactory.getPageNum(bpmModelQueryParam));
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String createModel(BpmModelParam bpmModelParam, String bpmnXml) {
|
||||
if (!ValidationUtils.isXmlNCName(bpmModelParam.getKey())) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_XML_ERROR);
|
||||
}
|
||||
// 校验流程标识已经存在
|
||||
Model keyModel = getModelByKey(bpmModelParam.getKey());
|
||||
if (keyModel != null) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_MODEL_REPEAT);
|
||||
}
|
||||
// 创建流程定义
|
||||
Model model = repositoryService.newModel();
|
||||
BpmModelConvert.INSTANCE.copyToCreateModel(model, bpmModelParam);
|
||||
model.setTenantId(FlowableUtils.getTenantId());
|
||||
// 保存流程定义
|
||||
repositoryService.saveModel(model);
|
||||
// 保存 BPMN XML
|
||||
saveModelBpmnXml(model, bpmnXml);
|
||||
return model.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务
|
||||
public void updateModel(BpmModelParam.BpmModelUpdateParam updateParam) {
|
||||
// 校验流程模型存在
|
||||
Model model = getModel(updateParam.getId());
|
||||
if (model == null) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST);
|
||||
}
|
||||
// 修改流程定义
|
||||
BpmModelConvert.INSTANCE.copyToUpdateModel(model, updateParam);
|
||||
// 更新模型
|
||||
repositoryService.saveModel(model);
|
||||
// 更新 BPMN XML
|
||||
saveModelBpmnXml(model, updateParam.getBpmnXml());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务
|
||||
public void deployModel(String id) {
|
||||
// 1.1 校验流程模型存在
|
||||
Model model = getModel(id);
|
||||
if (ObjectUtils.isEmpty(model)) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST);
|
||||
}
|
||||
// 1.2 校验流程图
|
||||
byte[] bpmnBytes = getModelBpmnXML(model.getId());
|
||||
validateBpmnXml(bpmnBytes);
|
||||
// 1.3 校验表单已配
|
||||
BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
|
||||
BpmForm form = validateFormConfig(metaInfo);
|
||||
|
||||
// 1.4 校验任务分配规则已配置
|
||||
taskCandidateInvoker.validateBpmnConfig(bpmnBytes);
|
||||
|
||||
// 2.1 创建流程定义
|
||||
String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, form);
|
||||
|
||||
// 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。
|
||||
updateProcessDefinitionSuspended(model.getDeploymentId());
|
||||
|
||||
// 2.3 更新 model 的 deploymentId,进行关联
|
||||
ProcessDefinition definition = processDefinitionService.getProcessDefinition(definitionId);
|
||||
model.setDeploymentId(definition.getDeploymentId());
|
||||
repositoryService.saveModel(model);
|
||||
}
|
||||
|
||||
private void validateBpmnXml(byte[] bpmnBytes) {
|
||||
BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
|
||||
if (bpmnModel == null) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST);
|
||||
}
|
||||
// 1. 没有 StartEvent
|
||||
StartEvent startEvent = BpmnModelUtils.getStartEvent(bpmnModel);
|
||||
if (startEvent == null) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_START_EVENT_NOT_EXIST);
|
||||
}
|
||||
// 2. 校验 UserTask 的 name 都配置了
|
||||
List<UserTask> userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
|
||||
UserTask userTask1 = userTasks.get(0);
|
||||
userTask1.getName();
|
||||
userTasks.forEach(userTask -> {
|
||||
if (StrUtil.isEmpty(userTask.getName())) {
|
||||
throw new BusinessException(BpmResponseEnum.MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteModel(String id) {
|
||||
// 校验流程模型存在
|
||||
Model model = getModel(id);
|
||||
if (model == null) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST);
|
||||
}
|
||||
// 执行删除
|
||||
repositoryService.deleteModel(id);
|
||||
// 禁用流程定义
|
||||
updateProcessDefinitionSuspended(model.getDeploymentId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateModelState(String id, Integer state) {
|
||||
// 1.1 校验流程模型存在
|
||||
Model model = getModel(id);
|
||||
if (model == null) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST);
|
||||
}
|
||||
// 1.2 校验流程定义存在
|
||||
ProcessDefinition definition = processDefinitionService.getProcessDefinitionByDeploymentId(model.getDeploymentId());
|
||||
if (definition == null) {
|
||||
throw new BusinessException(BpmResponseEnum.PROCESS_DEFINITION_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 2. 更新状态
|
||||
processDefinitionService.updateProcessDefinitionState(definition.getId(), state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmnModel getBpmnModelByDefinitionId(String processDefinitionId) {
|
||||
return repositoryService.getBpmnModel(processDefinitionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验流程表单已配置
|
||||
*
|
||||
* @param metaInfo 流程模型元数据
|
||||
* @return 表单配置
|
||||
*/
|
||||
private BpmForm validateFormConfig(BpmModelMetaInfoRespDTO metaInfo) {
|
||||
if (metaInfo == null || metaInfo.getFormType() == null) {
|
||||
throw new BusinessException(BpmResponseEnum.MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
|
||||
}
|
||||
// 校验表单存在
|
||||
if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) {
|
||||
if (metaInfo.getFormId() == null) {
|
||||
throw new BusinessException(BpmResponseEnum.MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
|
||||
}
|
||||
BpmForm form = bpmFormService.getById(metaInfo.getFormId());
|
||||
if (form == null) {
|
||||
throw new BusinessException(BpmResponseEnum.FORM_NOT_EXISTS);
|
||||
}
|
||||
return form;
|
||||
} else {
|
||||
if (StrUtil.isEmpty(metaInfo.getFormCustomCreatePath()) || StrUtil.isEmpty(metaInfo.getFormCustomViewPath())) {
|
||||
throw new BusinessException(BpmResponseEnum.MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void saveModelBpmnXml(Model model, String bpmnXml) {
|
||||
if (StrUtil.isEmpty(bpmnXml)) {
|
||||
return;
|
||||
}
|
||||
repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(bpmnXml));
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂起 deploymentId 对应的流程定义
|
||||
* <p>
|
||||
* 注意:这里一个 deploymentId 只关联一个流程定义
|
||||
*
|
||||
* @param deploymentId 流程发布Id
|
||||
*/
|
||||
private void updateProcessDefinitionSuspended(String deploymentId) {
|
||||
if (StrUtil.isEmpty(deploymentId)) {
|
||||
return;
|
||||
}
|
||||
ProcessDefinition oldDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(deploymentId);
|
||||
if (oldDefinition == null) {
|
||||
return;
|
||||
}
|
||||
processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), SuspensionState.SUSPENDED.getStateCode());
|
||||
}
|
||||
|
||||
private Model getModelByKey(String key) {
|
||||
return repositoryService.createModelQuery().modelKey(key).singleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Model getModel(String id) {
|
||||
return repositoryService.getModel(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getModelBpmnXML(String id) {
|
||||
return repositoryService.getModelEditorSource(id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package com.njcn.bpm.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.bpm.enums.BpmnModelConstants;
|
||||
import com.njcn.bpm.mapper.BpmCategoryMapper;
|
||||
import com.njcn.bpm.mapper.BpmProcessDefinitionInfoMapper;
|
||||
import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO;
|
||||
import com.njcn.bpm.pojo.param.BpmProcessDefinitionInfoParam;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import com.njcn.bpm.service.IBpmProcessDefinitionService;
|
||||
import com.njcn.bpm.utils.BeanUtils;
|
||||
import com.njcn.bpm.utils.FlowableUtils;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.common.engine.impl.db.SuspensionState;
|
||||
import org.flowable.engine.RepositoryService;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.Model;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.flowable.engine.repository.ProcessDefinitionQuery;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
|
||||
import static com.njcn.bpm.utils.CollectionUtils.addIfNotNull;
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
/**
|
||||
* 流程定义实现
|
||||
* 主要进行 Flowable {@link ProcessDefinition} 和 {@link Deployment} 的维护
|
||||
*
|
||||
* @author yunlongn
|
||||
* @author ZJQ
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class BpmProcessDefinitionServiceImpl extends ServiceImpl<BpmProcessDefinitionInfoMapper, BpmProcessDefinitionInfo> implements IBpmProcessDefinitionService {
|
||||
|
||||
@Resource
|
||||
private RepositoryService repositoryService;
|
||||
|
||||
|
||||
@Override
|
||||
public Page<ProcessDefinition> getProcessDefinitionPage(BpmProcessDefinitionInfoParam.BpmProcessDefinitionInfoQueryParam bpmProcessDefinitionInfoQueryParam) {
|
||||
ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
|
||||
if (StrUtil.isNotBlank(bpmProcessDefinitionInfoQueryParam.getKey())) {
|
||||
query.processDefinitionKey(bpmProcessDefinitionInfoQueryParam.getKey());
|
||||
}
|
||||
// 执行查询
|
||||
long count = query.count();
|
||||
if (count == 0) {
|
||||
return new Page<>();
|
||||
}
|
||||
int offset = PageFactory.getPageSize(bpmProcessDefinitionInfoQueryParam) * (PageFactory.getPageNum(bpmProcessDefinitionInfoQueryParam) - 1);
|
||||
List<ProcessDefinition> processDefinitionList = query.orderByProcessDefinitionVersion().desc()
|
||||
.listPage(offset, PageFactory.getPageSize(bpmProcessDefinitionInfoQueryParam));
|
||||
Page<ProcessDefinition> page = new Page<>();
|
||||
page.setRecords(processDefinitionList);
|
||||
page.setTotal(count);
|
||||
page.setSize(PageFactory.getPageSize(bpmProcessDefinitionInfoQueryParam));
|
||||
page.setCurrent(PageFactory.getPageNum(bpmProcessDefinitionInfoQueryParam));
|
||||
return page;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ProcessDefinition getProcessDefinition(String id) {
|
||||
return repositoryService.getProcessDefinition(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProcessDefinition> getProcessDefinitionList(Set<String> ids) {
|
||||
return repositoryService.createProcessDefinitionQuery().processDefinitionIds(ids).list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) {
|
||||
if (StrUtil.isEmpty(deploymentId)) {
|
||||
return null;
|
||||
}
|
||||
return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds) {
|
||||
if (CollUtil.isEmpty(deploymentIds)) {
|
||||
return emptyList();
|
||||
}
|
||||
return repositoryService.createProcessDefinitionQuery().deploymentIds(deploymentIds).list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessDefinition getActiveProcessDefinition(String key) {
|
||||
return repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).active().singleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Deployment> getDeploymentList(Set<String> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return emptyList();
|
||||
}
|
||||
List<Deployment> list = new ArrayList<>(ids.size());
|
||||
for (String id : ids) {
|
||||
addIfNotNull(list, getDeployment(id));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Deployment getDeployment(String id) {
|
||||
if (StrUtil.isEmpty(id)) {
|
||||
return null;
|
||||
}
|
||||
return repositoryService.createDeploymentQuery().deploymentId(id).singleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createProcessDefinition(Model model, BpmModelMetaInfoRespDTO modelMetaInfo,
|
||||
byte[] bpmnBytes, BpmForm form) {
|
||||
// 创建 Deployment 部署
|
||||
Deployment deploy = repositoryService.createDeployment()
|
||||
.key(model.getKey()).name(model.getName()).category(model.getCategory())
|
||||
.addBytes(model.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, bpmnBytes)
|
||||
.tenantId(FlowableUtils.getTenantId())
|
||||
.disableSchemaValidation() // 禁用 XML Schema 验证,因为有自定义的属性
|
||||
.deploy();
|
||||
|
||||
// 设置 ProcessDefinition 的 category 分类
|
||||
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
|
||||
.deploymentId(deploy.getId()).singleResult();
|
||||
repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory());
|
||||
// 注意 1,ProcessDefinition 的 key 和 name 是通过 BPMN 中的 <bpmn2:process /> 的 id 和 name 决定
|
||||
// 注意 2,目前该项目的设计上,需要保证 Model、Deployment、ProcessDefinition 使用相同的 key,保证关联性。
|
||||
// 否则,会导致 ProcessDefinition 的分页无法查询到。
|
||||
if (!Objects.equals(definition.getKey(), model.getKey())) {
|
||||
throw new BusinessException("流程定义的标识期望是(" + model.getKey() + "),当前是(" + definition.getKey() + "),请修改 BPMN 流程图");
|
||||
}
|
||||
if (!Objects.equals(definition.getName(), model.getName())) {
|
||||
throw new BusinessException("流程定义的名字期望是(" + model.getName() + "),当前是(" + definition.getName() + "),请修改 BPMN 流程图");
|
||||
}
|
||||
|
||||
// 插入拓展表
|
||||
BpmProcessDefinitionInfo definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfo.class);
|
||||
definitionDO.setModelId(model.getId());
|
||||
definitionDO.setProcessDefinitionId(definition.getId());
|
||||
if (form != null) {
|
||||
definitionDO.setFormFields(form.getFields());
|
||||
definitionDO.setFormConf(form.getConf());
|
||||
}
|
||||
this.baseMapper.insert(definitionDO);
|
||||
return definition.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProcessDefinitionState(String id, Integer state) {
|
||||
// 激活
|
||||
if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) {
|
||||
repositoryService.activateProcessDefinitionById(id, false, null);
|
||||
return;
|
||||
}
|
||||
// 挂起
|
||||
if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) {
|
||||
// suspendProcessInstances = false,进行中的任务,不进行挂起。
|
||||
// 原因:只要新的流程不允许发起即可,老流程继续可以执行。
|
||||
repositoryService.suspendProcessDefinitionById(id, false, null);
|
||||
return;
|
||||
}
|
||||
log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmnModel getProcessDefinitionBpmnModel(String id) {
|
||||
return repositoryService.getBpmnModel(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmProcessDefinitionInfo getProcessDefinitionInfo(String id) {
|
||||
LambdaQueryWrapper<BpmProcessDefinitionInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(BpmProcessDefinitionInfo::getProcessDefinitionId, id);
|
||||
return this.baseMapper.selectOne(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BpmProcessDefinitionInfo> getProcessDefinitionInfoList(Collection<String> ids) {
|
||||
LambdaQueryWrapper<BpmProcessDefinitionInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(BpmProcessDefinitionInfo::getProcessDefinitionId, ids)
|
||||
.eq(BpmProcessDefinitionInfo::getState, DataStateEnum.ENABLE.getCode());
|
||||
return this.baseMapper.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<ProcessDefinition> getProcessDefinitionListBySuspensionState(Integer suspensionState) {
|
||||
// 拼接查询条件
|
||||
ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
|
||||
if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), suspensionState)) {
|
||||
query.suspended();
|
||||
} else if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), suspensionState)) {
|
||||
query.active();
|
||||
}
|
||||
// 执行查询
|
||||
query.processDefinitionTenantId(FlowableUtils.getTenantId());
|
||||
return query.list();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.njcn.bpm.service.task;
|
||||
|
||||
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.njcn.bpm.utils.CollectionUtils.convertMap;
|
||||
|
||||
|
||||
/**
|
||||
* 流程实例 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IBpmProcessInstanceService {
|
||||
|
||||
/**
|
||||
* 获得流程实例
|
||||
*
|
||||
* @param id 流程实例的编号
|
||||
* @return 流程实例
|
||||
*/
|
||||
ProcessInstance getProcessInstance(String id);
|
||||
//
|
||||
// /**
|
||||
// * 获得流程实例列表
|
||||
// *
|
||||
// * @param ids 流程实例的编号集合
|
||||
// * @return 流程实例列表
|
||||
// */
|
||||
// List<ProcessInstance> getProcessInstances(Set<String> ids);
|
||||
//
|
||||
// /**
|
||||
// * 获得流程实例 Map
|
||||
// *
|
||||
// * @param ids 流程实例的编号集合
|
||||
// * @return 流程实例列表 Map
|
||||
// */
|
||||
// default Map<String, ProcessInstance> getProcessInstanceMap(Set<String> ids) {
|
||||
// return convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获得历史的流程实例
|
||||
// *
|
||||
// * @param id 流程实例的编号
|
||||
// * @return 历史的流程实例
|
||||
// */
|
||||
// HistoricProcessInstance getHistoricProcessInstance(String id);
|
||||
//
|
||||
// /**
|
||||
// * 获得历史的流程实例列表
|
||||
// *
|
||||
// * @param ids 流程实例的编号集合
|
||||
// * @return 历史的流程实例列表
|
||||
// */
|
||||
// List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids);
|
||||
//
|
||||
// /**
|
||||
// * 获得历史的流程实例 Map
|
||||
// *
|
||||
// * @param ids 流程实例的编号集合
|
||||
// * @return 历史的流程实例列表 Map
|
||||
// */
|
||||
// default Map<String, HistoricProcessInstance> getHistoricProcessInstanceMap(Set<String> ids) {
|
||||
// return convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获得流程实例的分页
|
||||
// *
|
||||
// * @param userId 用户编号
|
||||
// * @param pageReqVO 分页请求
|
||||
// * @return 流程实例的分页
|
||||
// */
|
||||
// PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId, @Valid BpmProcessInstancePageReqVO pageReqVO);
|
||||
//
|
||||
// /**
|
||||
// * 创建流程实例(提供给前端)
|
||||
// *
|
||||
// * @param userId 用户编号
|
||||
// * @param createReqVO 创建信息
|
||||
// * @return 实例的编号
|
||||
// */
|
||||
// String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO);
|
||||
//
|
||||
// /**
|
||||
// * 创建流程实例(提供给内部)
|
||||
// *
|
||||
// * @param userId 用户编号
|
||||
// * @param createReqDTO 创建信息
|
||||
// * @return 实例的编号
|
||||
// */
|
||||
// String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO);
|
||||
//
|
||||
// /**
|
||||
// * 发起人取消流程实例
|
||||
// *
|
||||
// * @param userId 用户编号
|
||||
// * @param cancelReqVO 取消信息
|
||||
// */
|
||||
// void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO);
|
||||
//
|
||||
// /**
|
||||
// * 管理员取消流程实例
|
||||
// *
|
||||
// * @param userId 用户编号
|
||||
// * @param cancelReqVO 取消信息
|
||||
// */
|
||||
// void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO);
|
||||
//
|
||||
// /**
|
||||
// * 更新 ProcessInstance 拓展记录为取消
|
||||
// *
|
||||
// * @param event 流程取消事件
|
||||
// */
|
||||
// void updateProcessInstanceWhenCancel(FlowableCancelledEvent event);
|
||||
//
|
||||
// /**
|
||||
// * 更新 ProcessInstance 拓展记录为完成
|
||||
// *
|
||||
// * @param instance 流程任务
|
||||
// */
|
||||
// void updateProcessInstanceWhenApprove(ProcessInstance instance);
|
||||
//
|
||||
// /**
|
||||
// * 更新 ProcessInstance 拓展记录为不通过
|
||||
// *
|
||||
// * @param id 流程编号
|
||||
// * @param reason 理由。例如说,审批不通过时,需要传递该值
|
||||
// */
|
||||
// void updateProcessInstanceReject(String id, String reason);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
package com.njcn.bpm.service.task.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.njcn.bpm.service.task.IBpmProcessInstanceService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.HistoryService;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.history.HistoricProcessInstanceQuery;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* 流程实例 Service 实现类
|
||||
*
|
||||
* ProcessDefinition & ProcessInstance & Execution & Task 的关系:
|
||||
* 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" />
|
||||
*
|
||||
* HistoricProcessInstance & ProcessInstance 的关系:
|
||||
* 1. <a href=" https://my.oschina.net/843294669/blog/71902" />
|
||||
*
|
||||
* 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class BpmProcessInstanceServiceImpl implements IBpmProcessInstanceService {
|
||||
|
||||
@Resource
|
||||
private RuntimeService runtimeService;
|
||||
@Resource
|
||||
private HistoryService historyService;
|
||||
|
||||
// @Resource
|
||||
// private BpmProcessDefinitionService processDefinitionService;
|
||||
// @Resource
|
||||
// private BpmMessageService messageService;
|
||||
//
|
||||
// @Resource
|
||||
// private AdminUserApi adminUserApi;
|
||||
//
|
||||
// @Resource
|
||||
// private BpmProcessInstanceEventPublisher processInstanceEventPublisher;
|
||||
|
||||
@Override
|
||||
public ProcessInstance getProcessInstance(String id) {
|
||||
return runtimeService.createProcessInstanceQuery()
|
||||
.includeProcessVariables()
|
||||
.processInstanceId(id)
|
||||
.singleResult();
|
||||
}
|
||||
//
|
||||
// @Override
|
||||
// public List<ProcessInstance> getProcessInstances(Set<String> ids) {
|
||||
// return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public HistoricProcessInstance getHistoricProcessInstance(String id) {
|
||||
// return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) {
|
||||
// return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId,
|
||||
// BpmProcessInstancePageReqVO pageReqVO) {
|
||||
// // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
|
||||
// HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery()
|
||||
// .includeProcessVariables()
|
||||
// .processInstanceTenantId(FlowableUtils.getTenantId())
|
||||
// .orderByProcessInstanceStartTime().desc();
|
||||
// if (userId != null) { // 【我的流程】菜单时,需要传递该字段
|
||||
// processInstanceQuery.startedBy(String.valueOf(userId));
|
||||
// } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段
|
||||
// processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId()));
|
||||
// }
|
||||
// if (StrUtil.isNotEmpty(pageReqVO.getName())) {
|
||||
// processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%");
|
||||
// }
|
||||
// if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) {
|
||||
// processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%");
|
||||
// }
|
||||
// if (StrUtil.isNotEmpty(pageReqVO.getCategory())) {
|
||||
// processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory());
|
||||
// }
|
||||
// if (pageReqVO.getStatus() != null) {
|
||||
// processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus());
|
||||
// }
|
||||
// if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) {
|
||||
// processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0]));
|
||||
// processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1]));
|
||||
// }
|
||||
// // 查询数量
|
||||
// long processInstanceCount = processInstanceQuery.count();
|
||||
// if (processInstanceCount == 0) {
|
||||
// return PageResult.empty(processInstanceCount);
|
||||
// }
|
||||
// // 查询列表
|
||||
// List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize());
|
||||
// return new PageResult<>(processInstanceList, processInstanceCount);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// @Transactional(rollbackFor = Exception.class)
|
||||
// public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
|
||||
// // 获得流程定义
|
||||
// ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
|
||||
// // 发起流程
|
||||
// return createProcessInstance0(userId, definition, createReqVO.getVariables(), null,
|
||||
// createReqVO.getStartUserSelectAssignees());
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
|
||||
// // 获得流程定义
|
||||
// ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
|
||||
// // 发起流程
|
||||
// return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(),
|
||||
// createReqDTO.getStartUserSelectAssignees());
|
||||
// }
|
||||
//
|
||||
// private String createProcessInstance0(Long userId, ProcessDefinition definition,
|
||||
// Map<String, Object> variables, String businessKey,
|
||||
// Map<String, List<Long>> startUserSelectAssignees) {
|
||||
// // 1.1 校验流程定义
|
||||
// if (definition == null) {
|
||||
// throw exception(PROCESS_DEFINITION_NOT_EXISTS);
|
||||
// }
|
||||
// if (definition.isSuspended()) {
|
||||
// throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
|
||||
// }
|
||||
// // 1.2 校验发起人自选审批人
|
||||
// validateStartUserSelectAssignees(definition, startUserSelectAssignees);
|
||||
//
|
||||
// // 2. 创建流程实例
|
||||
// if (variables == null) {
|
||||
// variables = new HashMap<>();
|
||||
// }
|
||||
// FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用
|
||||
// variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中
|
||||
// BpmProcessInstanceStatusEnum.RUNNING.getStatus());
|
||||
// if (CollUtil.isNotEmpty(startUserSelectAssignees)) {
|
||||
// variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees);
|
||||
// }
|
||||
// ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
|
||||
// .processDefinitionId(definition.getId())
|
||||
// .businessKey(businessKey)
|
||||
// .name(definition.getName().trim())
|
||||
// .variables(variables)
|
||||
// .start();
|
||||
// return instance.getId();
|
||||
// }
|
||||
//
|
||||
// private void validateStartUserSelectAssignees(ProcessDefinition definition, Map<String, List<Long>> startUserSelectAssignees) {
|
||||
// // 1. 获得发起人自选审批人的 UserTask 列表
|
||||
// BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId());
|
||||
// List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel);
|
||||
// if (CollUtil.isEmpty(userTaskList)) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 2. 校验发起人自选审批人的 UserTask 是否都配置了
|
||||
// userTaskList.forEach(userTask -> {
|
||||
// List<Long> assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(userTask.getId()) : null;
|
||||
// if (CollUtil.isEmpty(assignees)) {
|
||||
// throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, userTask.getName());
|
||||
// }
|
||||
// Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assignees);
|
||||
// assignees.forEach(assignee -> {
|
||||
// if (userMap.get(assignee) == null) {
|
||||
// throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, userTask.getName(), assignee);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
|
||||
// // 1.1 校验流程实例存在
|
||||
// ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
|
||||
// if (instance == null) {
|
||||
// throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
|
||||
// }
|
||||
// // 1.2 只能取消自己的
|
||||
// if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
|
||||
// throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
|
||||
// }
|
||||
//
|
||||
// // 2. 通过删除流程实例,实现流程实例的取消,
|
||||
// // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。
|
||||
// deleteProcessInstance(cancelReqVO.getId(),
|
||||
// BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason()));
|
||||
//
|
||||
// // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) {
|
||||
// // 1.1 校验流程实例存在
|
||||
// ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
|
||||
// if (instance == null) {
|
||||
// throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
|
||||
// }
|
||||
// // 1.2 管理员取消,不用校验是否为自己的
|
||||
// AdminUserRespDTO user = adminUserApi.getUser(userId);
|
||||
//
|
||||
// // 2. 通过删除流程实例,实现流程实例的取消,
|
||||
// // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。
|
||||
// deleteProcessInstance(cancelReqVO.getId(),
|
||||
// BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason()));
|
||||
//
|
||||
// // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) {
|
||||
// // 1. 判断是否为 Reject 不通过。如果是,则不进行更新.
|
||||
// // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了
|
||||
// if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 2. 更新流程实例 status
|
||||
// runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
|
||||
// BpmProcessInstanceStatusEnum.CANCEL.getStatus());
|
||||
//
|
||||
// // 3. 发送流程实例的状态事件
|
||||
// // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
|
||||
// HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId());
|
||||
// // 发送流程实例的状态事件
|
||||
// processInstanceEventPublisher.sendProcessInstanceResultEvent(
|
||||
// BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus()));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void updateProcessInstanceWhenApprove(ProcessInstance instance) {
|
||||
// // 1. 更新流程实例 status
|
||||
// runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
|
||||
// BpmProcessInstanceStatusEnum.APPROVE.getStatus());
|
||||
//
|
||||
// // 2. 发送流程被【通过】的消息
|
||||
// messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance));
|
||||
//
|
||||
// // 3. 发送流程实例的状态事件
|
||||
// // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
|
||||
// HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId());
|
||||
// processInstanceEventPublisher.sendProcessInstanceResultEvent(
|
||||
// BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus()));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// @Transactional(rollbackFor = Exception.class)
|
||||
// public void updateProcessInstanceReject(String id, String reason) {
|
||||
// // 1. 更新流程实例 status
|
||||
// runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus());
|
||||
//
|
||||
// // 2. 删除流程实例,以实现驳回任务时,取消整个审批流程
|
||||
// ProcessInstance processInstance = getProcessInstance(id);
|
||||
// deleteProcessInstance(id, StrUtil.format(BpmDeleteReasonEnum.REJECT_TASK.format(reason)));
|
||||
//
|
||||
// // 3. 发送流程被【不通过】的消息
|
||||
// messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(processInstance, reason));
|
||||
//
|
||||
// // 4. 发送流程实例的状态事件
|
||||
// processInstanceEventPublisher.sendProcessInstanceResultEvent(
|
||||
// BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus()));
|
||||
// }
|
||||
//
|
||||
// private void deleteProcessInstance(String id, String reason) {
|
||||
// runtimeService.deleteProcessInstance(id, reason);
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.njcn.bpm.strategy;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum;
|
||||
import com.njcn.bpm.utils.BpmnModelUtils;
|
||||
import com.njcn.bpm.utils.CollectionUtils;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.user.api.UserFeignClient;
|
||||
import com.njcn.user.pojo.po.User;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
public class BpmTaskCandidateInvoker {
|
||||
|
||||
private final Map<BpmTaskCandidateStrategyEnum, IBpmTaskCandidateStrategy> strategyMap = new HashMap<>();
|
||||
|
||||
private final UserFeignClient adminUserApi;
|
||||
|
||||
public BpmTaskCandidateInvoker(List<IBpmTaskCandidateStrategy> strategyList,
|
||||
UserFeignClient adminUserApi) {
|
||||
strategyList.forEach(strategy -> {
|
||||
IBpmTaskCandidateStrategy oldStrategy = strategyMap.put(strategy.getStrategy(), strategy);
|
||||
Assert.isNull(oldStrategy, "策略(%s) 重复", strategy.getStrategy());
|
||||
});
|
||||
this.adminUserApi = adminUserApi;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验流程模型的任务分配规则全部都配置了
|
||||
* 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去!
|
||||
*
|
||||
* @param bpmnBytes BPMN XML
|
||||
*/
|
||||
public void validateBpmnConfig(byte[] bpmnBytes) {
|
||||
BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
|
||||
assert bpmnModel != null;
|
||||
List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
|
||||
// 遍历所有的 UserTask,校验审批人配置
|
||||
userTaskList.forEach(userTask -> {
|
||||
// 1. 非空校验
|
||||
Integer strategy = BpmnModelUtils.parseCandidateStrategy(userTask);
|
||||
String param = BpmnModelUtils.parseCandidateParam(userTask);
|
||||
if (strategy == null) {
|
||||
throw new BusinessException("部署流程失败,原因:BPMN 流程图中,用户任务(" + userTask.getName() + ")的名字不存在");
|
||||
}
|
||||
IBpmTaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy);
|
||||
if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) {
|
||||
throw new BusinessException("部署流程失败,原因:用户任务(" + userTask.getName() + ")未配置审批人,请点击【流程设计】按钮,选择该它的【任务(审批人)】进行配置");
|
||||
}
|
||||
// 2. 具体策略校验
|
||||
getCandidateStrategy(strategy).validateParam(param);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算任务的候选人
|
||||
*
|
||||
* @param execution 执行任务
|
||||
* @return 用户编号集合
|
||||
*/
|
||||
public List<String> calculateUsers(DelegateExecution execution) {
|
||||
Integer strategy = BpmnModelUtils.parseCandidateStrategy(execution.getCurrentFlowElement());
|
||||
String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement());
|
||||
// 1.1 计算任务的候选人
|
||||
List<String> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
|
||||
// 1.2 移除被禁用的用户
|
||||
removeDisableUsers(userIds);
|
||||
|
||||
// 2. 校验是否有候选人
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
log.error("[calculateUsers][流程任务({}/{}/{}) 任务规则({}/{}) 找不到候选人]", execution.getId(),
|
||||
execution.getProcessDefinitionId(), execution.getCurrentActivityId(), strategy, param);
|
||||
throw new BusinessException("操作失败,原因:找不到任务的审批人!");
|
||||
}
|
||||
return userIds;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void removeDisableUsers(List<String> assigneeUserIds) {
|
||||
if (CollUtil.isEmpty(assigneeUserIds)) {
|
||||
return;
|
||||
}
|
||||
List<User> users = adminUserApi.getUserByIdList(assigneeUserIds).getData();
|
||||
Map<String, User> userMap = CollectionUtils.convertMap(users, User::getId);
|
||||
assigneeUserIds.removeIf(id -> {
|
||||
User user = userMap.get(id);
|
||||
return user == null || !DataStateEnum.ENABLE.getCode().equals(user.getState());
|
||||
});
|
||||
}
|
||||
|
||||
private IBpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
|
||||
BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
|
||||
Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);
|
||||
IBpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum);
|
||||
Assert.notNull(strategyObj, "策略(%s) 不存在", strategy);
|
||||
return strategyObj;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.njcn.bpm.strategy;
|
||||
|
||||
import com.njcn.user.api.UserFeignClient;
|
||||
import com.njcn.bpm.utils.StrUtils;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 角色 {@link IBpmTaskCandidateStrategy} 实现类
|
||||
*
|
||||
* @author kyle
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateRoleStrategy implements IBpmTaskCandidateStrategy {
|
||||
|
||||
@Resource
|
||||
private UserFeignClient userFeignClient;
|
||||
|
||||
// @Resource
|
||||
// private PermissionApi permissionApi;
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
return BpmTaskCandidateStrategyEnum.ROLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateParam(String param) {
|
||||
// Set<Long> roleIds = StrUtils.splitToLongSet(param);
|
||||
// roleApi.validRoleList(roleIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> calculateUsers(DelegateExecution execution, String param) {
|
||||
List<String> roleIds = StrUtils.splitToStringList(param);
|
||||
return userFeignClient.getUserIdByRoleId(roleIds).getData();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.njcn.bpm.strategy;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum;
|
||||
import com.njcn.bpm.service.task.IBpmProcessInstanceService;
|
||||
import com.njcn.bpm.utils.BpmnModelUtils;
|
||||
import com.njcn.bpm.utils.FlowableUtils;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateStartUserSelectStrategy implements IBpmTaskCandidateStrategy {
|
||||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private IBpmProcessInstanceService processInstanceService;
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
return BpmTaskCandidateStrategyEnum.START_USER_SELECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateParam(String param) {}
|
||||
|
||||
@Override
|
||||
public List<String> calculateUsers(DelegateExecution execution, String param) {
|
||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
|
||||
Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId());
|
||||
Map<String, List<String>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance);
|
||||
Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空",
|
||||
execution.getProcessInstanceId());
|
||||
// 获得审批人
|
||||
List<String> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId());
|
||||
return new ArrayList<>(assignees);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParamRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得发起人自选审批人的 UserTask 列表
|
||||
*
|
||||
* @param bpmnModel BPMN 模型
|
||||
* @return UserTask 列表
|
||||
*/
|
||||
public static List<UserTask> getStartUserSelectUserTaskList(BpmnModel bpmnModel) {
|
||||
if (bpmnModel == null) {
|
||||
return null;
|
||||
}
|
||||
List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
|
||||
if (CollUtil.isEmpty(userTaskList)) {
|
||||
return null;
|
||||
}
|
||||
userTaskList.removeIf(userTask -> !Objects.equals(BpmnModelUtils.parseCandidateStrategy(userTask),
|
||||
BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()));
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.njcn.bpm.strategy;
|
||||
|
||||
import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum;
|
||||
import com.njcn.bpm.utils.StrUtils;
|
||||
import com.njcn.user.api.UserFeignClient;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户 {@link IBpmTaskCandidateStrategy} 实现类
|
||||
*
|
||||
* @author kyle
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateUserStrategy implements IBpmTaskCandidateStrategy {
|
||||
|
||||
@Resource
|
||||
private UserFeignClient adminUserApi;
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
return BpmTaskCandidateStrategyEnum.USER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateParam(String param) {
|
||||
// adminUserApi.validateUserList(StrUtils.splitToLongSet(param));
|
||||
//暂时默认均有效,后期可以加强管理,todo...
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> calculateUsers(DelegateExecution execution, String param) {
|
||||
return StrUtils.splitToStringList(param);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.njcn.bpm.strategy;
|
||||
|
||||
import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* BPM 任务的候选人的策略接口
|
||||
*
|
||||
* 例如说:分配审批人
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IBpmTaskCandidateStrategy {
|
||||
|
||||
/**
|
||||
* 对应策略
|
||||
*
|
||||
* @return 策略
|
||||
*/
|
||||
BpmTaskCandidateStrategyEnum getStrategy();
|
||||
|
||||
/**
|
||||
* 校验参数
|
||||
*
|
||||
* @param param 参数
|
||||
*/
|
||||
void validateParam(String param);
|
||||
|
||||
/**
|
||||
* 基于执行任务,获得任务的候选用户们
|
||||
*
|
||||
* @param execution 执行任务
|
||||
* @return 用户编号集合
|
||||
*/
|
||||
List<String> calculateUsers(DelegateExecution execution, String param);
|
||||
|
||||
/**
|
||||
* 是否一定要输入参数
|
||||
*
|
||||
* @return 是否
|
||||
*/
|
||||
default boolean isParamRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.enums.BpmModelFormTypeEnum;
|
||||
import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO;
|
||||
import com.njcn.bpm.pojo.dto.PageResult;
|
||||
import com.njcn.bpm.pojo.param.BpmModelParam;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import com.njcn.bpm.pojo.vo.BpmModelRespVO;
|
||||
import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO;
|
||||
import org.flowable.common.engine.impl.db.SuspensionState;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.Model;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 流程模型 Convert
|
||||
*
|
||||
* @author yunlongn
|
||||
*/
|
||||
@Mapper
|
||||
public interface BpmModelConvert {
|
||||
|
||||
BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class);
|
||||
|
||||
default Page<BpmModelRespVO> buildModelPage(Page<Model> pageResult,
|
||||
Map<String, BpmForm> formMap,
|
||||
Map<String, BpmCategory> categoryMap, Map<String, Deployment> deploymentMap,
|
||||
Map<String, ProcessDefinition> processDefinitionMap) {
|
||||
List<BpmModelRespVO> list = CollectionUtils.convertList(pageResult.getRecords(), model -> {
|
||||
BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model);
|
||||
BpmForm form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null;
|
||||
BpmCategory category = categoryMap.get(model.getCategory());
|
||||
Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null;
|
||||
ProcessDefinition processDefinition = model.getDeploymentId() != null ? processDefinitionMap.get(model.getDeploymentId()) : null;
|
||||
return buildModel0(model, metaInfo, form, category, deployment, processDefinition);
|
||||
});
|
||||
Page<BpmModelRespVO> voPage = new Page<>();
|
||||
voPage.setRecords(list);
|
||||
voPage.setTotal(pageResult.getTotal());
|
||||
voPage.setSize(pageResult.getSize());
|
||||
voPage.setCurrent(pageResult.getCurrent());
|
||||
return voPage;
|
||||
}
|
||||
|
||||
default BpmModelRespVO buildModel(Model model,
|
||||
byte[] bpmnBytes) {
|
||||
BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model);
|
||||
BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null);
|
||||
if (ArrayUtil.isNotEmpty(bpmnBytes)) {
|
||||
modelVO.setBpmnXml(new String(bpmnBytes));
|
||||
}
|
||||
return modelVO;
|
||||
}
|
||||
|
||||
default BpmModelRespVO buildModel0(Model model,
|
||||
BpmModelMetaInfoRespDTO metaInfo, BpmForm form, BpmCategory category,
|
||||
Deployment deployment, ProcessDefinition processDefinition) {
|
||||
BpmModelRespVO modelRespVO = new BpmModelRespVO();
|
||||
modelRespVO.setId(model.getId());
|
||||
modelRespVO.setName(model.getName());
|
||||
modelRespVO.setKey(model.getKey());
|
||||
modelRespVO.setCategory(model.getCategory());
|
||||
modelRespVO.setCreateTime(DateUtils.of(model.getCreateTime()));
|
||||
// Form
|
||||
if (metaInfo != null) {
|
||||
modelRespVO.setFormType(metaInfo.getFormType());
|
||||
modelRespVO.setFormId(metaInfo.getFormId());
|
||||
modelRespVO.setFormCustomCreatePath(metaInfo.getFormCustomCreatePath());
|
||||
modelRespVO.setFormCustomViewPath(metaInfo.getFormCustomViewPath());
|
||||
modelRespVO.setIcon(metaInfo.getIcon());
|
||||
modelRespVO.setDescription(metaInfo.getDescription());
|
||||
}
|
||||
if (form != null) {
|
||||
modelRespVO.setFormId(form.getId());
|
||||
modelRespVO.setFormName(form.getName());
|
||||
}
|
||||
// Category
|
||||
if (category != null) {
|
||||
modelRespVO.setCategoryName(category.getName());
|
||||
}
|
||||
// ProcessDefinition
|
||||
if (processDefinition != null) {
|
||||
modelRespVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionInfoVO.class));
|
||||
modelRespVO.getProcessDefinition().setSuspensionState(processDefinition.isSuspended() ?
|
||||
SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
|
||||
if (deployment != null) {
|
||||
modelRespVO.getProcessDefinition().setDeploymentTime(DateUtils.of(deployment.getDeploymentTime()));
|
||||
}
|
||||
}
|
||||
return modelRespVO;
|
||||
}
|
||||
|
||||
default void copyToCreateModel(Model model, BpmModelParam bean) {
|
||||
model.setName(bean.getName());
|
||||
model.setKey(bean.getKey());
|
||||
model.setCategory(bean.getCategory());
|
||||
// model.setMetaInfo(buildMetaInfoStr(null,
|
||||
// null, bean.getDescription(),
|
||||
// null, null, null, null));
|
||||
//类型暂时写死为表单 todo...
|
||||
model.setMetaInfo(buildMetaInfoStr(buildMetaInfo(model),
|
||||
bean.getIcon(), bean.getDescription(),
|
||||
BpmModelFormTypeEnum.NORMAL.getType(), bean.getFormId(), bean.getFormCustomCreatePath(), bean.getFormCustomViewPath()));
|
||||
}
|
||||
|
||||
default void copyToUpdateModel(Model model, BpmModelParam.BpmModelUpdateParam bean) {
|
||||
model.setName(bean.getName());
|
||||
model.setCategory(bean.getCategory());
|
||||
model.setMetaInfo(buildMetaInfoStr(buildMetaInfo(model),
|
||||
bean.getIcon(), bean.getDescription(),
|
||||
bean.getFormType(), bean.getFormId(), bean.getFormCustomCreatePath(), bean.getFormCustomViewPath()));
|
||||
}
|
||||
|
||||
default String buildMetaInfoStr(BpmModelMetaInfoRespDTO metaInfo,
|
||||
String icon, String description,
|
||||
Integer formType, String formId, String formCustomCreatePath, String formCustomViewPath) {
|
||||
if (metaInfo == null) {
|
||||
metaInfo = new BpmModelMetaInfoRespDTO();
|
||||
}
|
||||
// 只有非空,才进行设置,避免更新时的覆盖
|
||||
if (StrUtil.isNotEmpty(icon)) {
|
||||
metaInfo.setIcon(icon);
|
||||
}
|
||||
if (StrUtil.isNotEmpty(description)) {
|
||||
metaInfo.setDescription(description);
|
||||
}
|
||||
if (Objects.nonNull(formType)) {
|
||||
metaInfo.setFormType(formType);
|
||||
metaInfo.setFormId(formId);
|
||||
metaInfo.setFormCustomCreatePath(formCustomCreatePath);
|
||||
metaInfo.setFormCustomViewPath(formCustomViewPath);
|
||||
}
|
||||
return JsonUtils.toJsonString(metaInfo);
|
||||
}
|
||||
|
||||
default BpmModelMetaInfoRespDTO buildMetaInfo(Model model) {
|
||||
return JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.common.engine.impl.db.SuspensionState;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Bpm 流程定义的 Convert
|
||||
*
|
||||
* @author yunlong.li
|
||||
*/
|
||||
@Mapper
|
||||
public interface BpmProcessDefinitionConvert {
|
||||
|
||||
BpmProcessDefinitionConvert INSTANCE = Mappers.getMapper(BpmProcessDefinitionConvert.class);
|
||||
|
||||
default Page<BpmProcessDefinitionInfoVO> buildProcessDefinitionPage(Page<ProcessDefinition> page,
|
||||
Map<String, Deployment> deploymentMap,
|
||||
Map<String, BpmProcessDefinitionInfo> processDefinitionInfoMap,
|
||||
Map<String, BpmForm> formMap,
|
||||
Map<String, BpmCategory> categoryMap) {
|
||||
List<BpmProcessDefinitionInfoVO> list = buildProcessDefinitionList(page.getRecords(), deploymentMap, processDefinitionInfoMap, formMap, categoryMap);
|
||||
Page<BpmProcessDefinitionInfoVO> voPage = new Page<>();
|
||||
voPage.setRecords(list);
|
||||
voPage.setTotal(page.getTotal());
|
||||
voPage.setSize(page.getSize());
|
||||
voPage.setCurrent(page.getCurrent());
|
||||
return voPage;
|
||||
}
|
||||
|
||||
default List<BpmProcessDefinitionInfoVO> buildProcessDefinitionList(List<ProcessDefinition> list,
|
||||
Map<String, Deployment> deploymentMap,
|
||||
Map<String, BpmProcessDefinitionInfo> processDefinitionInfoMap,
|
||||
Map<String, BpmForm> formMap,
|
||||
Map<String, BpmCategory> categoryMap) {
|
||||
return CollectionUtils.convertList(list, definition -> {
|
||||
Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class);
|
||||
BpmProcessDefinitionInfo processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfo.class);
|
||||
BpmForm form = null;
|
||||
if (processDefinitionInfo != null) {
|
||||
form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmForm.class);
|
||||
}
|
||||
BpmCategory category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategory.class);
|
||||
return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null, null);
|
||||
});
|
||||
}
|
||||
|
||||
default BpmProcessDefinitionInfoVO buildProcessDefinition(ProcessDefinition definition,
|
||||
Deployment deployment,
|
||||
BpmProcessDefinitionInfo processDefinitionInfo,
|
||||
BpmForm form,
|
||||
BpmCategory category,
|
||||
BpmnModel bpmnModel,
|
||||
List<UserTask> startUserSelectUserTaskList) {
|
||||
BpmProcessDefinitionInfoVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionInfoVO.class);
|
||||
respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
|
||||
// Deployment
|
||||
if (deployment != null) {
|
||||
respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime()));
|
||||
}
|
||||
// BpmProcessDefinitionInfoDO
|
||||
if (processDefinitionInfo != null) {
|
||||
copyTo(processDefinitionInfo, respVO);
|
||||
// Form
|
||||
if (form != null) {
|
||||
respVO.setFormName(form.getName());
|
||||
}
|
||||
}
|
||||
// Category
|
||||
if (category != null) {
|
||||
respVO.setCategoryName(category.getName());
|
||||
}
|
||||
// BpmnModel
|
||||
if (bpmnModel != null) {
|
||||
respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));
|
||||
respVO.setStartUserSelectTasks(BeanUtils.toBean(startUserSelectUserTaskList, BpmProcessDefinitionInfoVO.UserTask.class));
|
||||
}
|
||||
return respVO;
|
||||
}
|
||||
|
||||
@Mapping(source = "from.id", target = "to.id", ignore = true)
|
||||
void copyTo(BpmProcessDefinitionInfo from, @MappingTarget BpmProcessDefinitionInfoVO to);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.njcn.bpm.enums.BpmnModelConstants;
|
||||
import org.flowable.bpmn.converter.BpmnXMLConverter;
|
||||
import org.flowable.bpmn.model.Process;
|
||||
import org.flowable.bpmn.model.*;
|
||||
import org.flowable.common.engine.impl.util.io.BytesStreamSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 流程模型转操作工具类
|
||||
*/
|
||||
public class BpmnModelUtils {
|
||||
|
||||
public static Integer parseCandidateStrategy(FlowElement userTask) {
|
||||
return NumberUtils.parseInt(userTask.getAttributeValue(
|
||||
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
|
||||
}
|
||||
|
||||
public static String parseCandidateParam(FlowElement userTask) {
|
||||
return userTask.getAttributeValue(
|
||||
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点,获取入口连线
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @return 入口连线列表
|
||||
*/
|
||||
public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) {
|
||||
if (source instanceof FlowNode) {
|
||||
return ((FlowNode) source).getIncomingFlows();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点,获取出口连线
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @return 出口连线列表
|
||||
*/
|
||||
public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) {
|
||||
if (source instanceof FlowNode) {
|
||||
return ((FlowNode) source).getOutgoingFlows();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程元素信息
|
||||
*
|
||||
* @param model bpmnModel 对象
|
||||
* @param flowElementId 元素 ID
|
||||
* @return 元素信息
|
||||
*/
|
||||
public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) {
|
||||
Process process = model.getMainProcess();
|
||||
return process.getFlowElement(flowElementId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 BPMN 流程中,指定的元素们
|
||||
*
|
||||
* @param model 模型
|
||||
* @param clazz 指定元素。例如说,{@link UserTask}、{@link Gateway} 等等
|
||||
* @return 元素们
|
||||
*/
|
||||
public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
|
||||
List<T> result = new ArrayList<>();
|
||||
model.getProcesses().forEach(process -> {
|
||||
process.getFlowElements().forEach(flowElement -> {
|
||||
if (flowElement.getClass().isAssignableFrom(clazz)) {
|
||||
result.add((T) flowElement);
|
||||
}
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public static StartEvent getStartEvent(BpmnModel model) {
|
||||
Process process = model.getMainProcess();
|
||||
// 从 initialFlowElement 找
|
||||
FlowElement startElement = process.getInitialFlowElement();
|
||||
if (startElement instanceof StartEvent) {
|
||||
return (StartEvent) startElement;
|
||||
}
|
||||
// 从 flowElementList 找
|
||||
return (StartEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof StartEvent);
|
||||
}
|
||||
|
||||
public static BpmnModel getBpmnModel(byte[] bpmnBytes) {
|
||||
if (ArrayUtil.isEmpty(bpmnBytes)) {
|
||||
return null;
|
||||
}
|
||||
BpmnXMLConverter converter = new BpmnXMLConverter();
|
||||
// 补充说明:由于在 Flowable 中自定义了属性,所以 validateSchema 传递 false
|
||||
return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false);
|
||||
}
|
||||
|
||||
public static String getBpmnXml(BpmnModel model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
BpmnXMLConverter converter = new BpmnXMLConverter();
|
||||
return new String(converter.convertToXML(model));
|
||||
}
|
||||
|
||||
// ========== 遍历相关的方法 ==========
|
||||
|
||||
/**
|
||||
* 找到 source 节点之前的所有用户任务节点
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 已找到的用户任务节点
|
||||
* @return 用户任务节点 数组
|
||||
*/
|
||||
public static List<UserTask> getPreviousUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (source instanceof StartEvent && source.getSubProcess() != null) {
|
||||
userTaskList = getPreviousUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList);
|
||||
}
|
||||
|
||||
// 根据类型,获取入口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
|
||||
if (sequenceFlows == null) {
|
||||
return userTaskList;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 类型为用户节点,则新增父级节点
|
||||
if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement());
|
||||
}
|
||||
// 类型为子流程,则添加子流程开始节点出口处相连的节点
|
||||
if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
|
||||
// 获取子流程用户任务节点
|
||||
List<UserTask> childUserTaskList = findChildProcessUserTaskList((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (CollUtil.isNotEmpty(childUserTaskList)) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
userTaskList = getPreviousUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList);
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 迭代获取子流程用户任务节点
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 需要撤回的用户任务列表
|
||||
* @return 用户任务节点
|
||||
*/
|
||||
public static List<UserTask> findChildProcessUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
|
||||
// 根据类型,获取出口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
|
||||
if (sequenceFlows == null) {
|
||||
return userTaskList;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof UserTask) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
|
||||
continue;
|
||||
}
|
||||
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
|
||||
List<UserTask> childUserTaskList = findChildProcessUserTaskList((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (CollUtil.isNotEmpty(childUserTaskList)) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
userTaskList = findChildProcessUserTaskList(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList);
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行
|
||||
* 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param target 目标节点
|
||||
* @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set<String> visitedElements) {
|
||||
visitedElements = visitedElements == null ? new HashSet<>() : visitedElements;
|
||||
// 不能是开始事件和子流程
|
||||
if (source instanceof StartEvent && isInEventSubprocess(source)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 根据类型,获取入口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
|
||||
if (CollUtil.isEmpty(sequenceFlows)) {
|
||||
return true;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (visitedElements.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
visitedElements.add(sequenceFlow.getId());
|
||||
// 这条线路存在目标节点,这条线路完成,进入下个线路
|
||||
FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement();
|
||||
if (target.getId().equals(sourceFlowElement.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 如果目标节点为并行网关,则不继续
|
||||
if (sourceFlowElement instanceof ParallelGateway) {
|
||||
return false;
|
||||
}
|
||||
// 否则就继续迭代
|
||||
if (!isSequentialReachable(sourceFlowElement, target, visitedElements)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前节点是否属于不同的子流程
|
||||
*
|
||||
* @param flowElement 被判断的节点
|
||||
* @return true 表示属于子流程
|
||||
*/
|
||||
private static boolean isInEventSubprocess(FlowElement flowElement) {
|
||||
FlowElementsContainer flowElementsContainer = flowElement.getParentContainer();
|
||||
while (flowElementsContainer != null) {
|
||||
if (flowElementsContainer instanceof EventSubProcess) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flowElementsContainer instanceof FlowElement) {
|
||||
flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer();
|
||||
} else {
|
||||
flowElementsContainer = null;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param runTaskKeyList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 需要撤回的用户任务列表
|
||||
* @return 子级任务节点列表
|
||||
*/
|
||||
public static List<UserTask> iteratorFindChildUserTasks(FlowElement source, List<String> runTaskKeyList,
|
||||
Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (source instanceof StartEvent && source.getSubProcess() != null) {
|
||||
userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList);
|
||||
}
|
||||
|
||||
// 根据类型,获取出口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
|
||||
if (sequenceFlows == null) {
|
||||
return userTaskList;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
|
||||
continue;
|
||||
}
|
||||
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
|
||||
List<UserTask> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (CollUtil.isNotEmpty(childUserTaskList)) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList);
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import com.njcn.bpm.enums.BpmConstants;
|
||||
import org.flowable.common.engine.api.delegate.Expression;
|
||||
import org.flowable.common.engine.api.variable.VariableContainer;
|
||||
import org.flowable.common.engine.impl.el.ExpressionManager;
|
||||
import org.flowable.common.engine.impl.identity.Authentication;
|
||||
import org.flowable.engine.ProcessEngineConfiguration;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
|
||||
import org.flowable.engine.impl.util.CommandContextUtil;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.TaskInfo;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Flowable 相关的工具方法
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class FlowableUtils {
|
||||
|
||||
// ========== User 相关的工具方法 ==========
|
||||
|
||||
public static void setAuthenticatedUserId(Long userId) {
|
||||
Authentication.setAuthenticatedUserId(String.valueOf(userId));
|
||||
}
|
||||
|
||||
public static void clearAuthenticatedUserId() {
|
||||
Authentication.setAuthenticatedUserId(null);
|
||||
}
|
||||
|
||||
public static String getTenantId() {
|
||||
return ProcessEngineConfiguration.NO_TENANT_ID;
|
||||
}
|
||||
|
||||
// ========== Execution 相关的工具方法 ==========
|
||||
|
||||
/**
|
||||
* 格式化多实例(并签、或签)的 collectionVariable 变量(多实例对应的多审批人列表)
|
||||
*
|
||||
* @param activityId 活动编号
|
||||
* @return collectionVariable 变量
|
||||
*/
|
||||
public static String formatExecutionCollectionVariable(String activityId) {
|
||||
return activityId + "_assignees";
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化多实例(并签、或签)的 collectionElementVariable 变量(当前实例对应的一个审批人)
|
||||
*
|
||||
* @param activityId 活动编号
|
||||
* @return collectionElementVariable 变量
|
||||
*/
|
||||
public static String formatExecutionCollectionElementVariable(String activityId) {
|
||||
return activityId + "_assignee";
|
||||
}
|
||||
|
||||
// ========== ProcessInstance 相关的工具方法 ==========
|
||||
|
||||
public static Integer getProcessInstanceStatus(ProcessInstance processInstance) {
|
||||
return getProcessInstanceStatus(processInstance.getProcessVariables());
|
||||
}
|
||||
|
||||
public static Integer getProcessInstanceStatus(HistoricProcessInstance processInstance) {
|
||||
return getProcessInstanceStatus(processInstance.getProcessVariables());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程实例的状态
|
||||
*
|
||||
* @param processVariables 流程实例的 variables
|
||||
* @return 状态
|
||||
*/
|
||||
private static Integer getProcessInstanceStatus(Map<String, Object> processVariables) {
|
||||
return (Integer) processVariables.get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程实例的表单
|
||||
*
|
||||
* @param processInstance 流程实例
|
||||
* @return 表单
|
||||
*/
|
||||
public static Map<String, Object> getProcessInstanceFormVariable(HistoricProcessInstance processInstance) {
|
||||
Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables());
|
||||
filterProcessInstanceFormVariable(formVariables);
|
||||
return formVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤流程实例的表单
|
||||
*
|
||||
* 为什么要过滤?目前使用 processVariables 存储所有流程实例的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示
|
||||
*
|
||||
* @param processVariables 流程实例的 variables
|
||||
* @return 过滤后的表单
|
||||
*/
|
||||
public static Map<String, Object> filterProcessInstanceFormVariable(Map<String, Object> processVariables) {
|
||||
processVariables.remove(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
|
||||
return processVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程实例的发起用户选择的审批人 Map
|
||||
*
|
||||
* @param processInstance 流程实例
|
||||
* @return 发起用户选择的审批人 Map
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map<String, List<String>> getStartUserSelectAssignees(ProcessInstance processInstance) {
|
||||
return (Map<String, List<String>>) processInstance.getProcessVariables().get(
|
||||
BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
|
||||
}
|
||||
|
||||
// ========== Task 相关的工具方法 ==========
|
||||
|
||||
/**
|
||||
* 获得任务的状态
|
||||
*
|
||||
* @param task 任务
|
||||
* @return 状态
|
||||
*/
|
||||
public static Integer getTaskStatus(TaskInfo task) {
|
||||
return (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得任务的审批原因
|
||||
*
|
||||
* @param task 任务
|
||||
* @return 审批原因
|
||||
*/
|
||||
public static String getTaskReason(TaskInfo task) {
|
||||
return (String) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_REASON);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得任务的表单
|
||||
*
|
||||
* @param task 任务
|
||||
* @return 表单
|
||||
*/
|
||||
public static Map<String, Object> getTaskFormVariable(TaskInfo task) {
|
||||
Map<String, Object> formVariables = new HashMap<>(task.getTaskLocalVariables());
|
||||
filterTaskFormVariable(formVariables);
|
||||
return formVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤任务的表单
|
||||
*
|
||||
* 为什么要过滤?目前使用 taskLocalVariables 存储所有任务的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示
|
||||
*
|
||||
* @param taskLocalVariables 任务的 taskLocalVariables
|
||||
* @return 过滤后的表单
|
||||
*/
|
||||
public static Map<String, Object> filterTaskFormVariable(Map<String, Object> taskLocalVariables) {
|
||||
taskLocalVariables.remove(BpmConstants.TASK_VARIABLE_STATUS);
|
||||
taskLocalVariables.remove(BpmConstants.TASK_VARIABLE_REASON);
|
||||
return taskLocalVariables;
|
||||
}
|
||||
|
||||
// ========== Expression 相关的工具方法 ==========
|
||||
|
||||
public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) {
|
||||
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
|
||||
assert processEngineConfiguration != null;
|
||||
ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
|
||||
assert expressionManager != null;
|
||||
Expression expression = expressionManager.createExpression(expressionString);
|
||||
return expression.getValue(variableContainer);
|
||||
}
|
||||
|
||||
}
|
||||
202
pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/JsonUtils.java
Normal file
202
pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/JsonUtils.java
Normal file
@@ -0,0 +1,202 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JSON 工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
public class JsonUtils {
|
||||
|
||||
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
static {
|
||||
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值
|
||||
objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 objectMapper 属性
|
||||
* <p>
|
||||
* 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean
|
||||
*
|
||||
* @param objectMapper ObjectMapper 对象
|
||||
*/
|
||||
public static void init(ObjectMapper objectMapper) {
|
||||
JsonUtils.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String toJsonString(Object object) {
|
||||
return objectMapper.writeValueAsString(object);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static byte[] toJsonByte(Object object) {
|
||||
return objectMapper.writeValueAsBytes(object);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String toJsonPrettyString(Object object) {
|
||||
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, clazz);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, String path, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JsonNode treeNode = objectMapper.readTree(text);
|
||||
JsonNode pathNode = treeNode.path(path);
|
||||
return objectMapper.readValue(pathNode.toString(), clazz);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, Type type) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type));
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串解析成指定类型的对象
|
||||
* 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下,
|
||||
* 如果 text 没有 class 属性,则会报错。此时,使用这个方法,可以解决。
|
||||
*
|
||||
* @param text 字符串
|
||||
* @param clazz 类型
|
||||
* @return 对象
|
||||
*/
|
||||
public static <T> T parseObject2(String text, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
return JSONUtil.toBean(text, clazz);
|
||||
}
|
||||
|
||||
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
|
||||
if (ArrayUtil.isEmpty(bytes)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(bytes, clazz);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", bytes, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return objectMapper.readValue(text, typeReference);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 JSON 字符串成指定类型的对象,如果解析失败,则返回 null
|
||||
*
|
||||
* @param text 字符串
|
||||
* @param typeReference 类型引用
|
||||
* @return 指定类型的对象
|
||||
*/
|
||||
public static <T> T parseObjectQuietly(String text, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return objectMapper.readValue(text, typeReference);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> List<T> parseArray(String text, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> List<T> parseArray(String text, String path, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JsonNode treeNode = objectMapper.readTree(text);
|
||||
JsonNode pathNode = treeNode.path(path);
|
||||
return objectMapper.readValue(pathNode.toString(), objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonNode parseTree(String text) {
|
||||
try {
|
||||
return objectMapper.readTree(text);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonNode parseTree(byte[] text) {
|
||||
try {
|
||||
return objectMapper.readTree(text);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isJson(String text) {
|
||||
return JSONUtil.isTypeJSON(text);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.text.StrPool;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class StrUtils {
|
||||
|
||||
public static String maxLength(CharSequence str, int maxLength) {
|
||||
return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定字符串是否以任何一个字符串开始
|
||||
* 给定字符串和数组为空都返回 false
|
||||
*
|
||||
* @param str 给定字符串
|
||||
* @param prefixes 需要检测的开始字符串
|
||||
* @since 3.0.6
|
||||
*/
|
||||
public static boolean startWithAny(String str, Collection<String> prefixes) {
|
||||
if (StrUtil.isEmpty(str) || ArrayUtil.isEmpty(prefixes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (CharSequence suffix : prefixes) {
|
||||
if (StrUtil.startWith(str, suffix, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<Long> splitToLong(String value, CharSequence separator) {
|
||||
long[] longs = StrUtil.splitToLong(value, separator);
|
||||
return Arrays.stream(longs).boxed().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static Set<Long> splitToLongSet(String value) {
|
||||
return splitToLongSet(value, StrPool.COMMA);
|
||||
}
|
||||
|
||||
public static Set<Long> splitToLongSet(String value, CharSequence separator) {
|
||||
long[] longs = StrUtil.splitToLong(value, separator);
|
||||
return Arrays.stream(longs).boxed().collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static List<Integer> splitToInteger(String value, CharSequence separator) {
|
||||
int[] integers = StrUtil.splitToInt(value, separator);
|
||||
return Arrays.stream(integers).boxed().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除字符串中,包含指定字符串的行
|
||||
*
|
||||
* @param content 字符串
|
||||
* @param sequence 包含的字符串
|
||||
* @return 移除后的字符串
|
||||
*/
|
||||
public static String removeLineContains(String content, String sequence) {
|
||||
if (StrUtil.isEmpty(content) || StrUtil.isEmpty(sequence)) {
|
||||
return content;
|
||||
}
|
||||
return Arrays.stream(content.split("\n"))
|
||||
.filter(line -> !line.contains(sequence))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
public static List<String> splitToStringList(String value) {
|
||||
return StrUtil.split(value, StrPool.COMMA);
|
||||
}
|
||||
}
|
||||
69
pqs-bpm/bpm-boot/src/main/resources/bootstrap.yml
Normal file
69
pqs-bpm/bpm-boot/src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,69 @@
|
||||
#当前服务的基本信息
|
||||
microservice:
|
||||
ename: @artifactId@
|
||||
name: '@name@'
|
||||
version: @version@
|
||||
sentinel:
|
||||
url: @sentinel.url@
|
||||
gateway:
|
||||
url: @gateway.url@
|
||||
server:
|
||||
port: 10321
|
||||
#feign接口开启服务熔断降级处理
|
||||
feign:
|
||||
sentinel:
|
||||
enabled: true
|
||||
spring:
|
||||
application:
|
||||
name: @artifactId@
|
||||
#nacos注册中心以及配置中心的指定
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
ip: @service.server.url@
|
||||
server-addr: @nacos.url@
|
||||
namespace: @nacos.namespace@
|
||||
config:
|
||||
server-addr: @nacos.url@
|
||||
namespace: @nacos.namespace@
|
||||
file-extension: yaml
|
||||
shared-configs:
|
||||
- data-id: share-config.yaml
|
||||
refresh: true
|
||||
- data-Id: bpm-config.yaml
|
||||
refresh: true
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 100MB
|
||||
max-request-size: 100MB
|
||||
|
||||
flowable:
|
||||
database-schema-update: false
|
||||
db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置
|
||||
check-process-definitions: false # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程
|
||||
history-level: full # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数
|
||||
async-executor-activate: false
|
||||
|
||||
#项目日志的配置
|
||||
logging:
|
||||
config: http://@nacos.url@/nacos/v1/cs/configs?tenant=@nacos.namespace@&group=DEFAULT_GROUP&dataId=logback.xml
|
||||
level:
|
||||
root: info
|
||||
|
||||
|
||||
#mybatis配置信息
|
||||
mybatis-plus:
|
||||
type-aliases-package: com.njcn.bpm.pojo
|
||||
|
||||
gw:
|
||||
url: dwzyywzt-pms3-proxy.com
|
||||
code: 13B9B47F1E483324E05338297A0A0595
|
||||
|
||||
mqtt:
|
||||
client-id: @artifactId@${random.value}
|
||||
|
||||
|
||||
|
||||
|
||||
28
pqs-bpm/pom.xml
Normal file
28
pqs-bpm/pom.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>pqs</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
|
||||
<artifactId>pqs-bpm</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>bpm-api</module>
|
||||
<module>bpm-boot</module>
|
||||
</modules>
|
||||
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -21,7 +21,7 @@ public class GenerateCode {
|
||||
|
||||
private static final String TARGET_DIR = "D://code";
|
||||
|
||||
private static final String DB_URL = "jdbc:mysql://192.168.1.24:13306/pqsinfo_hn";
|
||||
private static final String DB_URL = "jdbc:mysql://192.168.1.24:13306/pqsinfo_hn_process";
|
||||
// private static final String DB_URL = "jdbc:oracle:thin:@192.168.1.170:1521:pqsbase";
|
||||
|
||||
private static final String USERNAME = "root";
|
||||
@@ -30,9 +30,9 @@ public class GenerateCode {
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<Module> modules = Stream.of(
|
||||
new Module("hongawen", "com.njcn.advance.govern", "voltage", Stream.of(
|
||||
"sg_harmonic_file"
|
||||
).collect(Collectors.toList()), "")
|
||||
new Module("hongawen", "com.njcn.process", "workflow", Stream.of(
|
||||
"wf_deploy_form"
|
||||
).collect(Collectors.toList()), "wf_")
|
||||
).collect(Collectors.toList());
|
||||
generateJavaFile(modules);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import java.util.stream.Stream;
|
||||
@Slf4j
|
||||
public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
private final static String[] WHITE_PARAMETER_NAME = {"password", "mxContent", "docContent", "bgImage","fileContent","flowableXml"};
|
||||
private final static String[] WHITE_PARAMETER_NAME = {"password", "mxContent", "docContent", "bgImage","fileContent","flowableXml","bpmnXml"};
|
||||
|
||||
|
||||
public XssRequestWrapper(HttpServletRequest request) {
|
||||
|
||||
@@ -21,16 +21,16 @@
|
||||
<dependencies>
|
||||
|
||||
<!--pq-device-boot和pms-device-boot只能选择一个-->
|
||||
<!-- <dependency>
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>pq-device-boot</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>pms-device-boot</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.njcn</groupId>-->
|
||||
<!-- <artifactId>pms-device-boot</artifactId>-->
|
||||
<!-- <version>${project.version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
|
||||
@@ -134,7 +134,7 @@ public class OracleTerminalExcel implements Serializable {
|
||||
@Excel(name = "合同号", width = 15)
|
||||
private String contract;
|
||||
|
||||
@Excel(name = "电度功能", replace = {"关闭_0", "开启_1"}, width = 15)
|
||||
@Excel(name = "电度功能", replace = {"关闭_0", "开启_1","null_0"}, width = 15)
|
||||
private Integer electroplate;
|
||||
|
||||
@Excel(name = "母线", width = 15)
|
||||
|
||||
@@ -172,6 +172,13 @@ spring:
|
||||
filters:
|
||||
- SwaggerHeaderFilter
|
||||
- StripPrefix=1
|
||||
- id: bpm-boot
|
||||
uri: lb://bpm-boot
|
||||
predicates:
|
||||
- Path=/bpm-boot/**
|
||||
filters:
|
||||
- SwaggerHeaderFilter
|
||||
- StripPrefix=1
|
||||
#项目日志的配置
|
||||
logging:
|
||||
config: http://@nacos.url@/nacos/v1/cs/configs?tenant=@nacos.namespace@&group=DEFAULT_GROUP&dataId=logback.xml
|
||||
@@ -211,7 +218,11 @@ whitelist:
|
||||
#- /event-boot/**
|
||||
#- /quality-boot/**
|
||||
#- /harmonic-prepare/**
|
||||
#- /process-boot/**
|
||||
- /process-boot/**
|
||||
- /bpm-boot/**
|
||||
- /system-boot/**
|
||||
- /user-boot/**
|
||||
- /user-boot/user/listAllUserByDeptId
|
||||
|
||||
|
||||
mqtt:
|
||||
|
||||
@@ -31,7 +31,8 @@ spring:
|
||||
- data-id: share-config.yaml
|
||||
refresh: true
|
||||
#数据中心使用
|
||||
# - data-Id: share-config-datasource-db.yaml
|
||||
# - data-Id: share-config-datasource-db.yaml
|
||||
# refresh: true
|
||||
#PMS使用
|
||||
- data-Id: share-config-harmonic-db.yaml
|
||||
refresh: true
|
||||
|
||||
@@ -26,7 +26,24 @@ public enum ProcessResponseEnum {
|
||||
|
||||
SUPV_PLAN_REPEAT("A00568","监督计划名称已存在"),
|
||||
NO_UPDATE("A00568","该申请单非新建,驳回状态不准许更新"),
|
||||
REPEAT_NAME_CATEGORY("A00568","流程表单名称重复"),
|
||||
REPEAT_CODE_CATEGORY("A00568","流程表单编号重复"),
|
||||
|
||||
FORM_NOT_EXIST("A00568","流程表单不存在"),
|
||||
|
||||
MODEL_NO_EXIST("A00568","流程模型不存在"),
|
||||
|
||||
NO_MODEL_XML("A00568","未获取到流程模型设计"),
|
||||
|
||||
NO_XML("A00568","请先设计流程图"),
|
||||
|
||||
NO_STARTER_MODEL_XML("A00568","开始节点不存在,请检查流程设计是否有误"),
|
||||
|
||||
MODEL_HAS_NO_FORM("A00568","请配置流程表单"),
|
||||
|
||||
LATEST_VERSION("A00568","当前版本已是最新版!"),
|
||||
|
||||
PROCESS_DATASOURCE_MISS("A00568","数据源没找到!"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class FlowProcDefDto implements Serializable {
|
||||
@ApiModelProperty("流程key")
|
||||
private String flowKey;
|
||||
|
||||
@ApiModelProperty("流程分类")
|
||||
@ApiModelProperty("流程表单")
|
||||
private String category;
|
||||
|
||||
@ApiModelProperty("配置表单名称")
|
||||
|
||||
@@ -17,7 +17,7 @@ public class FlowSaveXmlVo implements Serializable {
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 流程分类
|
||||
* 流程表单
|
||||
*/
|
||||
private String category;
|
||||
|
||||
|
||||
@@ -38,4 +38,8 @@ public class FlowServiceFactory {
|
||||
@Resource
|
||||
protected ProcessEngine processEngine;
|
||||
|
||||
// @Resource
|
||||
// protected FormService formService;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,11 +3,13 @@ package com.njcn.system.controller;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.dto.SimpleDTO;
|
||||
import com.njcn.common.pojo.dto.SimpleTreeDTO;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
@@ -60,6 +62,19 @@ public class DictTypeController extends BaseController {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有字典类型数据
|
||||
*/
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@GetMapping("/listAll")
|
||||
@ApiOperation("查询所有字典类型数据")
|
||||
public HttpResult<List<DictType>> listAll() {
|
||||
String methodDescribe = getMethodDescribe("listAll");
|
||||
LogUtil.njcnDebug(log, "{}", methodDescribe);
|
||||
List<DictType> dictTypeList = dictTypeService.list(new LambdaQueryWrapper<DictType>().eq(DictType::getState, DataStateEnum.ENABLE.getCode()));
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, dictTypeList, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新增字典类型
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.njcn.user.api;
|
||||
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.ServerInfo;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
@@ -160,4 +161,7 @@ public interface DeptFeignClient {
|
||||
*/
|
||||
@GetMapping("/getAllDept")
|
||||
HttpResult<List<Dept>> getAllDept();
|
||||
|
||||
@GetMapping("/orgTreeSelector")
|
||||
HttpResult<List<Tree<String>>> orgTreeSelector();
|
||||
}
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
package com.njcn.user.api;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.ServerInfo;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.HttpResultUtil;
|
||||
import com.njcn.user.api.fallback.UserFeignClientFallbackFactory;
|
||||
import com.njcn.user.pojo.dto.UserDTO;
|
||||
import com.njcn.user.pojo.po.User;
|
||||
import com.njcn.user.pojo.vo.UserVO;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -18,7 +28,7 @@ import java.util.List;
|
||||
* @version 1.0.0
|
||||
* @date 2021年05月08日 15:11
|
||||
*/
|
||||
@FeignClient(value = ServerInfo.USER,path = "/user",fallbackFactory = UserFeignClientFallbackFactory.class,contextId = "user")
|
||||
@FeignClient(value = ServerInfo.USER, path = "/user", fallbackFactory = UserFeignClientFallbackFactory.class, contextId = "user")
|
||||
public interface UserFeignClient {
|
||||
|
||||
/**
|
||||
@@ -37,10 +47,11 @@ public interface UserFeignClient {
|
||||
* @return 用户基本信息
|
||||
*/
|
||||
@GetMapping("/getUserByPhone/{phone}")
|
||||
HttpResult<UserDTO> getUserByPhone(@PathVariable("phone")String phone);
|
||||
HttpResult<UserDTO> getUserByPhone(@PathVariable("phone") String phone);
|
||||
|
||||
/**
|
||||
* 认证后根据用户名判断用户状态
|
||||
*
|
||||
* @param loginName 登录名
|
||||
* @return 校验结果
|
||||
*/
|
||||
@@ -58,6 +69,7 @@ public interface UserFeignClient {
|
||||
|
||||
/**
|
||||
* 根据用户id集合查询用户信息
|
||||
*
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
@@ -70,6 +82,7 @@ public interface UserFeignClient {
|
||||
|
||||
/**
|
||||
* 根据部门ids查询接收短信通知的用户信息
|
||||
*
|
||||
* @param deptId
|
||||
* @return
|
||||
*/
|
||||
@@ -78,14 +91,24 @@ public interface UserFeignClient {
|
||||
|
||||
/**
|
||||
* 根据部门ids查询用户信息
|
||||
*
|
||||
* @param deptId
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/getUserInfoByDeptIds")
|
||||
HttpResult<List<User>> getUserInfoByDeptIds(@RequestBody List<String> deptId);
|
||||
|
||||
/**
|
||||
* 根据角色id查询用户id
|
||||
*
|
||||
* @param roleId 角色id集合
|
||||
*/
|
||||
@PostMapping("/getUserIdByRoleId")
|
||||
HttpResult<List<String>> getUserIdByRoleId(@RequestBody List<String> roleId);
|
||||
|
||||
/**
|
||||
* 根据角色Code集合查询用户信息
|
||||
*
|
||||
* @param roleCode
|
||||
* @return
|
||||
*/
|
||||
@@ -95,4 +118,7 @@ public interface UserFeignClient {
|
||||
@GetMapping("/getUserById")
|
||||
HttpResult<UserVO> getUserById(@RequestParam("id") String id);
|
||||
|
||||
@PostMapping("/getUserListByIds")
|
||||
HttpResult<List<User>> getUserListByIds(@RequestBody List<String> ids);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.njcn.user.api.fallback;
|
||||
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
@@ -146,6 +147,12 @@ public class DeptFeignClientFallbackFactory implements FallbackFactory<DeptFeign
|
||||
log.error("{}异常,降级处理,异常为:{}","获取所有单位:",cause.toString());
|
||||
throw new BusinessException(finalExceptionEnum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResult<List<Tree<String>>> orgTreeSelector() {
|
||||
log.error("{}异常,降级处理,异常为:{}","获取所有单位树:",cause.toString());
|
||||
throw new BusinessException(finalExceptionEnum);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.njcn.user.api.fallback;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
@@ -86,6 +87,12 @@ public class UserFeignClientFallbackFactory implements FallbackFactory<UserFeign
|
||||
throw new BusinessException(finalExceptionEnum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResult<List<String>> getUserIdByRoleId(List<String> roleId) {
|
||||
log.error("{}异常,降级处理,异常为:{}","根据角色ids查询用户id",cause.toString());
|
||||
throw new BusinessException(finalExceptionEnum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResult<List<User>> getUserListByRoleCode(String roleCode) {
|
||||
log.error("{}异常,降级处理,异常为:{}","根据角色Code集合查询用户信息",cause.toString());
|
||||
@@ -98,6 +105,11 @@ public class UserFeignClientFallbackFactory implements FallbackFactory<UserFeign
|
||||
throw new BusinessException(finalExceptionEnum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResult<List<User>> getUserListByIds(List<String> ids) {
|
||||
log.error("{}异常,降级处理,异常为:{}","根据用户id集合获取用户集合",cause.toString());
|
||||
throw new BusinessException(finalExceptionEnum);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package com.njcn.user.controller;
|
||||
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import cn.hutool.core.lang.tree.TreeNode;
|
||||
import cn.hutool.core.lang.tree.TreeUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
@@ -14,6 +19,7 @@ import com.njcn.system.pojo.dto.AreaTreeDTO;
|
||||
import com.njcn.user.pojo.dto.DeptDTO;
|
||||
import com.njcn.user.pojo.param.DeptParam;
|
||||
import com.njcn.user.pojo.po.Dept;
|
||||
import com.njcn.user.pojo.po.Role;
|
||||
import com.njcn.user.pojo.vo.DeptAllTreeVO;
|
||||
import com.njcn.user.pojo.vo.DeptTreeVO;
|
||||
import com.njcn.user.pojo.vo.DeptVO;
|
||||
@@ -29,6 +35,7 @@ import springfox.documentation.annotations.ApiIgnore;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -155,6 +162,17 @@ public class DeptController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@PostMapping("/getDeptListByIds")
|
||||
@ApiOperation("根据部门id集合查询部门信息")
|
||||
@ApiImplicitParam(name = "ids", value = "部门id集合", required = true)
|
||||
public HttpResult<List<Dept>> getDeptListByIds(@RequestBody List<String> ids) {
|
||||
String methodDescribe = getMethodDescribe("getDeptListByIds");
|
||||
List<Dept> users = deptService.list((new LambdaQueryWrapper<Dept>().in(CollUtil.isNotEmpty(ids), Dept::getId, ids)));
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断是否有关联信息
|
||||
*/
|
||||
@@ -187,6 +205,19 @@ public class DeptController extends BaseController {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门树
|
||||
*/
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@GetMapping("/deptTreeSelector")
|
||||
@ApiOperation("部门信息树")
|
||||
public HttpResult<List<Tree<String>>> deptTreeSelector() {
|
||||
String methodDescribe = getMethodDescribe("deptTreeSelector");
|
||||
List<Tree<String>> result = deptService.orgTreeSelector();
|
||||
//删除返回失败,查不到数据返回空数组,兼容治理项目没有部门直接报错的bug
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@PostMapping("/existMonitorDeptTree")
|
||||
@@ -511,6 +542,19 @@ public class DeptController extends BaseController {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的部门集合
|
||||
* @author xy
|
||||
* @date 2023/12/11
|
||||
*/
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@GetMapping("/orgTreeSelector")
|
||||
@ApiOperation("获取所有单位树")
|
||||
public HttpResult<List<Tree<String>>> orgTreeSelector() {
|
||||
String methodDescribe = getMethodDescribe("orgTreeSelector");
|
||||
List<Tree<String>> result = deptService.orgTreeSelector();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
/*++++++++++++++++++++++++++++++++pms专用+++++++++++++++++++++++++++++++end*/
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.njcn.user.controller;
|
||||
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
@@ -12,6 +14,7 @@ import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.user.pojo.param.DeptParam;
|
||||
import com.njcn.user.pojo.param.RoleParam;
|
||||
import com.njcn.user.pojo.po.Role;
|
||||
import com.njcn.user.pojo.po.User;
|
||||
import com.njcn.user.pojo.vo.RoleVO;
|
||||
import com.njcn.user.service.IRoleService;
|
||||
import io.swagger.annotations.Api;
|
||||
@@ -114,6 +117,16 @@ public class RoleController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@PostMapping("/getRoleListByIds")
|
||||
@ApiOperation("根据角色id集合查询角色信息")
|
||||
@ApiImplicitParam(name = "ids", value = "角色id集合", required = true)
|
||||
public HttpResult<List<Role>> getRoleListByIds(@RequestBody List<String> ids) {
|
||||
String methodDescribe = getMethodDescribe("getRoleListByIds");
|
||||
List<Role> users = roleService.list((new LambdaQueryWrapper<Role>().in(CollUtil.isNotEmpty(ids), Role::getId, ids)));
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色id查询相关联的资源和组件
|
||||
*/
|
||||
@@ -145,5 +158,29 @@ public class RoleController extends BaseController {
|
||||
List<Role> result = roleService.selectRoleDetail(id);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有角色
|
||||
*/
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@GetMapping("/allRoleList")
|
||||
@ApiOperation("查询所有角色")
|
||||
public HttpResult<Object> allRoleList() {
|
||||
String methodDescribe = getMethodDescribe("allRoleList");
|
||||
List<Role> result = roleService.allRoleList();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有角色
|
||||
*/
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@GetMapping("/simpleList")
|
||||
@ApiOperation("查询所有角色作为下拉框")
|
||||
public HttpResult<Object> simpleList() {
|
||||
String methodDescribe = getMethodDescribe("simpleList");
|
||||
List<Role> result = roleService.simpleList();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.njcn.user.controller;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
@@ -26,6 +28,7 @@ import com.njcn.user.pojo.dto.UserDTO;
|
||||
import com.njcn.user.pojo.param.UserInfoParm;
|
||||
import com.njcn.user.pojo.param.UserParam;
|
||||
import com.njcn.user.pojo.param.UserPasswordParam;
|
||||
import com.njcn.user.pojo.po.Role;
|
||||
import com.njcn.user.pojo.po.User;
|
||||
import com.njcn.user.pojo.vo.UserVO;
|
||||
import com.njcn.user.service.IUserService;
|
||||
@@ -48,6 +51,7 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -194,18 +198,19 @@ public class UserController extends BaseController {
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPLOAD)
|
||||
@PostMapping("/uploadImage")
|
||||
@ApiOperation("上传头像")
|
||||
public HttpResult<MinIoUploadResDTO> uploadImage( MultipartFile issuesFile){
|
||||
public HttpResult<MinIoUploadResDTO> uploadImage(MultipartFile issuesFile) {
|
||||
String methodDescribe = getMethodDescribe("uploadImage");
|
||||
String filePath = userService.uploadImage(issuesFile);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,new MinIoUploadResDTO(issuesFile.getOriginalFilename(),filePath), methodDescribe);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, new MinIoUploadResDTO(issuesFile.getOriginalFilename(), filePath), methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPLOAD)
|
||||
@PostMapping("/getUrl")
|
||||
@ApiOperation("获取头像url")
|
||||
public HttpResult<String> getUrl(@RequestParam("headSculpture") String headSculpture){
|
||||
public HttpResult<String> getUrl(@RequestParam("headSculpture") String headSculpture) {
|
||||
String methodDescribe = getMethodDescribe("getUrl");
|
||||
String url = userService.getUrl(headSculpture);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,url, methodDescribe);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, url, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
@@ -221,6 +226,16 @@ public class UserController extends BaseController {
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@GetMapping("/listAllUserByDeptId")
|
||||
@ApiOperation("查询部门下所有状态正常的用户")
|
||||
public HttpResult<List<UserVO>> listAllUserByDeptId(String deptId) {
|
||||
String methodDescribe = getMethodDescribe("listAllUserByDeptId");
|
||||
List<UserVO> list = userService.listAllUserByDeptId(deptId);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@PostMapping("/exportUser")
|
||||
@ApiOperation("用户数据")
|
||||
@@ -228,19 +243,18 @@ public class UserController extends BaseController {
|
||||
public HttpResult<String> exportUser(@RequestBody @Validated UserParam.UserQueryParam queryParam) {
|
||||
String methodDescribe = getMethodDescribe("exportUser");
|
||||
LogUtil.njcnDebug(log, "{},查询数据为:{}", methodDescribe, queryParam);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, userService.exportUser(queryParam,methodDescribe), methodDescribe);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, userService.exportUser(queryParam, methodDescribe), methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON,operateType = OperateType.DOWNLOAD)
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.DOWNLOAD)
|
||||
@GetMapping("/exportUserExcel")
|
||||
@ApiOperation("导出用户数据")
|
||||
@ApiImplicitParam(name = "filePath", value = "报表路径", required = true)
|
||||
public void exportUserExcel(String filePath, HttpServletResponse response) {
|
||||
PoiUtil.exportFileByAbsolutePath(filePath,response);
|
||||
PoiUtil.exportFileByAbsolutePath(filePath, response);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@PostMapping("/checkUserList")
|
||||
@ApiOperation("审核用户列表")
|
||||
@@ -337,7 +351,7 @@ public class UserController extends BaseController {
|
||||
//秘钥先删除再添加
|
||||
redisUtil.delete(loginName + ip);
|
||||
// 保存私钥到 redis
|
||||
redisUtil.saveByKeyWithExpire(loginName + ip, privateKey, 5*60L);
|
||||
redisUtil.saveByKeyWithExpire(loginName + ip, privateKey, 5 * 60L);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, publicKey, methodDescribe);
|
||||
}
|
||||
|
||||
@@ -356,6 +370,7 @@ public class UserController extends BaseController {
|
||||
|
||||
/**
|
||||
* 密码二次确认
|
||||
*
|
||||
* @param password 确认密码
|
||||
*/
|
||||
@OperateInfo
|
||||
@@ -366,7 +381,7 @@ public class UserController extends BaseController {
|
||||
String methodDescribe = getMethodDescribe("passwordConfirm");
|
||||
LogUtil.njcnDebug(log, "{},用户输入的密码:{}", methodDescribe, password);
|
||||
boolean result = userService.passwordConfirm(password);
|
||||
if (result){
|
||||
if (result) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe);
|
||||
@@ -393,6 +408,7 @@ public class UserController extends BaseController {
|
||||
|
||||
/**
|
||||
* 用于激活处于休眠、锁定、密码过期的用户
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @return
|
||||
*/
|
||||
@@ -404,7 +420,7 @@ public class UserController extends BaseController {
|
||||
String methodDescribe = getMethodDescribe("activateUser");
|
||||
LogUtil.njcnDebug(log, "{},用户输入的id:{}", methodDescribe, id);
|
||||
boolean result = userService.activateUser(id);
|
||||
if (result){
|
||||
if (result) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe);
|
||||
@@ -418,10 +434,10 @@ public class UserController extends BaseController {
|
||||
public HttpResult<List<User>> getUserByIdList(@RequestBody List<String> ids) {
|
||||
String methodDescribe = getMethodDescribe("getUserByIdList");
|
||||
List<User> users = userService.list((new LambdaQueryWrapper<User>()
|
||||
.in(CollUtil.isNotEmpty(ids),User::getId,ids)
|
||||
.in(CollUtil.isNotEmpty(ids), User::getId, ids)
|
||||
.isNotNull(User::getDeptId)
|
||||
.ne(User::getDeptId,"")
|
||||
.ne(User::getState,"0")
|
||||
.ne(User::getDeptId, "")
|
||||
.ne(User::getState, "0")
|
||||
));
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe);
|
||||
}
|
||||
@@ -433,12 +449,21 @@ public class UserController extends BaseController {
|
||||
public HttpResult<List<User>> appuserByIdList(@RequestBody List<String> ids) {
|
||||
String methodDescribe = getMethodDescribe("appuserByIdList");
|
||||
List<User> users = userService.list((new LambdaQueryWrapper<User>()
|
||||
.in(CollUtil.isNotEmpty(ids),User::getId,ids)
|
||||
|
||||
.in(CollUtil.isNotEmpty(ids), User::getId, ids)
|
||||
));
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@PostMapping("/getUserListByIds")
|
||||
@ApiOperation("根据用户id集合查询用户信息")
|
||||
@ApiImplicitParam(name = "ids", value = "用户id集合", required = true)
|
||||
public HttpResult<List<User>> getUserListByIds(@RequestBody List<String> ids) {
|
||||
String methodDescribe = getMethodDescribe("getUserListByIds");
|
||||
List<User> users = userService.list((new LambdaQueryWrapper<User>().in(CollUtil.isNotEmpty(ids), User::getId, ids)));
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@PostMapping("/getMarketList")
|
||||
@ApiOperation("获取营销用户列表")
|
||||
@@ -452,6 +477,7 @@ public class UserController extends BaseController {
|
||||
|
||||
/**
|
||||
* 获取同级部门用户,以及下级部门所有用户
|
||||
*
|
||||
* @author cdf
|
||||
* @date 2023/7/31
|
||||
*/
|
||||
@@ -468,6 +494,7 @@ public class UserController extends BaseController {
|
||||
|
||||
/**
|
||||
* 根据部门ids查询用户信息
|
||||
*
|
||||
* @param deptId
|
||||
* @return
|
||||
*/
|
||||
@@ -478,9 +505,9 @@ public class UserController extends BaseController {
|
||||
public HttpResult<List<User>> getUserByDeptIds(@RequestBody List<String> deptId) {
|
||||
String methodDescribe = getMethodDescribe("getUserByDeptIds");
|
||||
List<User> users = userService.list(new LambdaQueryWrapper<User>()
|
||||
.in(User::getDeptId,deptId)
|
||||
.eq(User::getSmsNotice,1)
|
||||
.eq(User::getState,1)
|
||||
.in(User::getDeptId, deptId)
|
||||
.eq(User::getSmsNotice, 1)
|
||||
.eq(User::getState, 1)
|
||||
);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe);
|
||||
}
|
||||
@@ -492,14 +519,25 @@ public class UserController extends BaseController {
|
||||
public HttpResult<List<User>> getUserInfoByDeptIds(@RequestBody List<String> deptId) {
|
||||
String methodDescribe = getMethodDescribe("getUserInfoByDeptIds");
|
||||
List<User> users = userService.list(new LambdaQueryWrapper<User>()
|
||||
.in(User::getDeptId,deptId)
|
||||
.in(User::getDeptId, deptId)
|
||||
.eq(User::getState, DataStateEnum.ENABLE.getCode())
|
||||
);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@PostMapping("/getUserIdByRoleId")
|
||||
@ApiOperation("根据角色id查询用户id")
|
||||
@ApiImplicitParam(name = "roleId", value = "用户角色id", required = true)
|
||||
public HttpResult<List<String>> getUserIdByRoleId(@RequestBody List<String> roleId) {
|
||||
String methodDescribe = getMethodDescribe("getUserInfoByRoleId");
|
||||
List<String> userId = userService.getUserIdByRoleId(roleId);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, userId, methodDescribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取同级部门用户,以及下级部门所有用户
|
||||
*
|
||||
* @author cdf
|
||||
* @date 2023/7/31
|
||||
*/
|
||||
@@ -516,6 +554,7 @@ public class UserController extends BaseController {
|
||||
|
||||
/**
|
||||
* 根据角色类型获取用户 角色类型 type0:超级管理员;1:管理员;2:普通用户' 3:'审核角色',
|
||||
*
|
||||
* @author cdf
|
||||
* @date 2024/3/29
|
||||
*/
|
||||
@@ -528,5 +567,18 @@ public class UserController extends BaseController {
|
||||
List<User> users = userService.getUserByRoleType(roleType);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询所有用户
|
||||
*/
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@GetMapping("/simpleList")
|
||||
@ApiOperation("查询所有用户作为下拉框")
|
||||
public HttpResult<Object> simpleList() {
|
||||
String methodDescribe = getMethodDescribe("simpleList");
|
||||
List<User> result = userService.simpleList();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.njcn.user.service;
|
||||
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.system.pojo.dto.AreaTreeDTO;
|
||||
@@ -247,4 +248,7 @@ public interface IDeptService extends IService<Dept> {
|
||||
* @Date: 2023/12/11 14:50
|
||||
*/
|
||||
List<Dept> getAllDept();
|
||||
|
||||
List<Tree<String>> orgTreeSelector();
|
||||
|
||||
}
|
||||
|
||||
@@ -93,4 +93,7 @@ public interface IRoleService extends IService<Role> {
|
||||
*/
|
||||
Role getRoleByCode(String code);
|
||||
|
||||
List<Role> allRoleList();
|
||||
|
||||
List<Role> simpleList();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.user.pojo.param.UserInfoParm;
|
||||
import com.njcn.user.pojo.param.UserParam;
|
||||
import com.njcn.user.pojo.po.Role;
|
||||
import com.njcn.user.pojo.po.User;
|
||||
import com.njcn.user.pojo.dto.UserDTO;
|
||||
import com.njcn.user.pojo.vo.UserVO;
|
||||
@@ -190,4 +191,9 @@ public interface IUserService extends IService<User> {
|
||||
|
||||
List<User> getUserByRoleType(Integer roleType);
|
||||
|
||||
List<UserVO> listAllUserByDeptId(String deptId);
|
||||
|
||||
List<String> getUserIdByRoleId(List<String> roleId);
|
||||
|
||||
List<User> simpleList();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ package com.njcn.user.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import cn.hutool.core.lang.tree.TreeNode;
|
||||
import cn.hutool.core.lang.tree.TreeUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
@@ -65,8 +68,6 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
private final CommLedgerDeptClient commLedgerDeptClient;
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Page<DeptVO> listDept(DeptParam.QueryParam queryParam) {
|
||||
QueryWrapper<DeptVO> queryWrapper = new QueryWrapper<>();
|
||||
@@ -82,7 +83,7 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
queryWrapper.orderBy(true, queryParam.getOrderBy().equals(DbConstant.ASC), StrUtil.toUnderlineCase(queryParam.getSortBy()));
|
||||
} else {
|
||||
//没有排序参数,默认根据sort字段排序,没有排序字段的,根据updateTime更新时间排序
|
||||
queryWrapper.orderBy(true, queryParam.getOrderBy().equals(DbConstant.ASC), "sys_dept.sort");
|
||||
queryWrapper.orderBy(true, true, "sys_dept.sort");
|
||||
}
|
||||
}
|
||||
queryWrapper.ne("sys_dept.state", DataStateEnum.DELETED.getCode());
|
||||
@@ -105,7 +106,7 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
} else {
|
||||
String pids = "," + deptParam.getPid();
|
||||
String pid = this.baseMapper.getIdString(deptParam.getPid());
|
||||
if(StrUtil.isBlank(deptParam.getPid())){
|
||||
if (StrUtil.isBlank(deptParam.getPid())) {
|
||||
throw new BusinessException(UserResponseEnum.DEPT_PID_EXCEPTION);
|
||||
}
|
||||
|
||||
@@ -114,7 +115,7 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
}
|
||||
//默认为正常状态
|
||||
dept.setState(DataStateEnum.ENABLE.getCode());
|
||||
if (StrUtil.isBlank(dept.getCode())){
|
||||
if (StrUtil.isBlank(dept.getCode())) {
|
||||
dept.setCode(dept.getId());
|
||||
}
|
||||
return this.save(dept);
|
||||
@@ -129,7 +130,7 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
Dept deptTem = this.getDeptById(updateParam.getId());
|
||||
|
||||
this.updateById(dept);
|
||||
if(!updateParam.getName().equals(deptTem.getName())){
|
||||
if (!updateParam.getName().equals(deptTem.getName())) {
|
||||
//修改了部门名称需要修改台账信息中的单位部门
|
||||
commLedgerDeptClient.update(dept);
|
||||
}
|
||||
@@ -181,20 +182,20 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
@Override
|
||||
public List<DeptTreeVO> existMonitorDeptTree() {
|
||||
List<Dept> deptTem = commLedgerDeptClient.existMonitorDeptTree().getData();
|
||||
if(CollectionUtil.isEmpty(deptTem)){
|
||||
if (CollectionUtil.isEmpty(deptTem)) {
|
||||
throw new BusinessException("当前没有部门存在监测点");
|
||||
}
|
||||
|
||||
List<String> useMonitorIds = deptTem.stream().map(Dept::getId).collect(Collectors.toList());
|
||||
List<String> resultDeptId = deptTem.stream().map(item->item.getPids().split(",")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());
|
||||
List<String> resultDeptId = deptTem.stream().map(item -> item.getPids().split(",")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());
|
||||
resultDeptId.addAll(useMonitorIds);
|
||||
|
||||
List<Integer> deptType = WebUtil.filterDeptType();
|
||||
String deptIndex = RequestUtil.getDeptIndex();
|
||||
List<DeptTreeVO> deptList = this.baseMapper.getDeptTree(deptIndex, deptType);
|
||||
List<DeptTreeVO> finalDeptList = deptList.stream().filter(item->resultDeptId.contains(item.getId())).collect(Collectors.toList());
|
||||
List<DeptTreeVO> lastList = finalDeptList.stream().peek(item->{
|
||||
if(useMonitorIds.contains(item.getId())){
|
||||
List<DeptTreeVO> finalDeptList = deptList.stream().filter(item -> resultDeptId.contains(item.getId())).collect(Collectors.toList());
|
||||
List<DeptTreeVO> lastList = finalDeptList.stream().peek(item -> {
|
||||
if (useMonitorIds.contains(item.getId())) {
|
||||
item.setLevel(2);
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
@@ -225,7 +226,7 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
List<String> areaTreeVO = deptArea();
|
||||
HttpResult<List<AreaTreeDTO>> areaTreeDTOS = areaFeignClient.areaDeptTree(id, type);
|
||||
List<AreaTreeDTO> list = areaTreeDTOS.getData();
|
||||
if(CollectionUtils.isEmpty(list)){
|
||||
if (CollectionUtils.isEmpty(list)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
for (AreaTreeDTO areaTreeVOList : list) {
|
||||
@@ -251,14 +252,14 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
List<AreaTreeDTO> result = new ArrayList<>();
|
||||
List<String> deptBindAreaList = deptArea();
|
||||
List<Area> areaList = areaFeignClient.getPidAreaList(id, type).getData();
|
||||
areaList.forEach(item->{
|
||||
areaList.forEach(item -> {
|
||||
AreaTreeDTO areaTreeDTO = new AreaTreeDTO();
|
||||
areaTreeDTO.setId(item.getId());
|
||||
areaTreeDTO.setPid(item.getPid());
|
||||
if(deptBindAreaList.contains(item.getId())){
|
||||
if (deptBindAreaList.contains(item.getId())) {
|
||||
areaTreeDTO.setName(item.getName() + "(已被绑定)");
|
||||
areaTreeDTO.setIsFalse(1);
|
||||
}else {
|
||||
} else {
|
||||
areaTreeDTO.setName(item.getName());
|
||||
areaTreeDTO.setIsFalse(0);
|
||||
}
|
||||
@@ -340,7 +341,7 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
@Override
|
||||
public Dept getDeptByCode(String deptCode) {
|
||||
LambdaQueryWrapper<Dept> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(Dept::getCode,deptCode).eq(Dept::getState,DataStateEnum.ENABLE.getCode());
|
||||
lambdaQueryWrapper.eq(Dept::getCode, deptCode).eq(Dept::getState, DataStateEnum.ENABLE.getCode());
|
||||
return this.baseMapper.selectOne(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@@ -406,7 +407,7 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
@Override
|
||||
public List<String> getDepSonSelfCodetByDeptId(String id) {
|
||||
Dept dept = this.getDeptById(id);
|
||||
if(Objects.isNull(dept)){
|
||||
if (Objects.isNull(dept)) {
|
||||
throw new BusinessException(UserResponseEnum.DEPT_MISSING);
|
||||
}
|
||||
List<String> sonIds = this.baseMapper.getDeptSonSlfeIds(id);
|
||||
@@ -418,11 +419,11 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
public List<String> getDepSonSelfCodetByCode(String code) {
|
||||
List<String> codes = new ArrayList<>();
|
||||
Dept dept = this.getDeptByCode(code);
|
||||
if(Objects.isNull(dept)){
|
||||
if (Objects.isNull(dept)) {
|
||||
throw new BusinessException(UserResponseEnum.DEPT_MISSING);
|
||||
}
|
||||
List<DeptDTO> deptList = this.baseMapper.getDeptDescendantIndexes(dept.getId(), Stream.of(0,1).collect(Collectors.toList()));
|
||||
if(CollectionUtil.isNotEmpty(deptList)){
|
||||
List<DeptDTO> deptList = this.baseMapper.getDeptDescendantIndexes(dept.getId(), Stream.of(0, 1).collect(Collectors.toList()));
|
||||
if (CollectionUtil.isNotEmpty(deptList)) {
|
||||
codes = deptList.stream().map(DeptDTO::getCode).collect(Collectors.toList());
|
||||
codes.add(dept.getCode());
|
||||
}
|
||||
@@ -441,10 +442,10 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
@Override
|
||||
public List<Dept> getDirectSonSelf(String deptId) {
|
||||
LambdaQueryWrapper<Dept> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.and (Wrapper-> Wrapper.
|
||||
eq (Dept::getPid,deptId).
|
||||
or ().
|
||||
eq(Dept::getId,deptId)).eq (Dept::getState, DataStateEnum.ENABLE.getCode());
|
||||
lambdaQueryWrapper.and(Wrapper -> Wrapper.
|
||||
eq(Dept::getPid, deptId).
|
||||
or().
|
||||
eq(Dept::getId, deptId)).eq(Dept::getState, DataStateEnum.ENABLE.getCode());
|
||||
// lambdaQueryWrapper.eq(Dept::getPid,deptId).eq(Dept::getState, DataStateEnum.ENABLE.getCode()).
|
||||
// or().eq(Dept::getId,deptId);
|
||||
return this.list(lambdaQueryWrapper);
|
||||
@@ -453,14 +454,14 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
@Override
|
||||
public List<Dept> getSpecialDeptList() {
|
||||
LambdaQueryWrapper<Dept> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(Dept::getSpecialType,1).eq(Dept::getState, DataStateEnum.ENABLE.getCode());
|
||||
lambdaQueryWrapper.eq(Dept::getSpecialType, 1).eq(Dept::getState, DataStateEnum.ENABLE.getCode());
|
||||
return this.list(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dept getRootDept(){
|
||||
public Dept getRootDept() {
|
||||
LambdaQueryWrapper<Dept> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(Dept::getState,DataStateEnum.ENABLE.getCode()).eq(Dept::getPid,'0');
|
||||
lambdaQueryWrapper.eq(Dept::getState, DataStateEnum.ENABLE.getCode()).eq(Dept::getPid, '0');
|
||||
return this.getOne(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@@ -474,23 +475,23 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
*/
|
||||
@Override
|
||||
public List<DeptDTO> getDepSonDetailByDeptId(String deptId) {
|
||||
List<Dept> result = new ArrayList<> ();
|
||||
List<Dept> result = new ArrayList<>();
|
||||
|
||||
LambdaQueryWrapper<Dept> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq (Dept::getPid, deptId).
|
||||
eq (Dept::getState,DataStateEnum.ENABLE.getCode());
|
||||
result = this.baseMapper.selectList (lambdaQueryWrapper);
|
||||
if(CollectionUtils.isEmpty (result)){
|
||||
lambdaQueryWrapper.eq(Dept::getPid, deptId).
|
||||
eq(Dept::getState, DataStateEnum.ENABLE.getCode());
|
||||
result = this.baseMapper.selectList(lambdaQueryWrapper);
|
||||
if (CollectionUtils.isEmpty(result)) {
|
||||
LambdaQueryWrapper<Dept> deptLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
deptLambdaQueryWrapper.eq (Dept::getId, deptId).
|
||||
eq (Dept::getState,DataStateEnum.ENABLE.getCode());
|
||||
result = this.baseMapper.selectList (deptLambdaQueryWrapper);
|
||||
deptLambdaQueryWrapper.eq(Dept::getId, deptId).
|
||||
eq(Dept::getState, DataStateEnum.ENABLE.getCode());
|
||||
result = this.baseMapper.selectList(deptLambdaQueryWrapper);
|
||||
}
|
||||
List<DeptDTO> collect = result.stream ( ).map (temp -> {
|
||||
DeptDTO deptDTO = new DeptDTO ( );
|
||||
BeanUtils.copyProperties (temp, deptDTO);
|
||||
List<DeptDTO> collect = result.stream().map(temp -> {
|
||||
DeptDTO deptDTO = new DeptDTO();
|
||||
BeanUtils.copyProperties(temp, deptDTO);
|
||||
return deptDTO;
|
||||
}).collect (Collectors.toList ( ));
|
||||
}).collect(Collectors.toList());
|
||||
return collect;
|
||||
}
|
||||
|
||||
@@ -503,16 +504,26 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
|
||||
@Override
|
||||
public List<Dept> getDeptByCodeList(List<String> list) {
|
||||
return this.lambdaQuery().in(Dept::getCode,list).eq(Dept::getState,DataStateEnum.ENABLE.getCode()).list();
|
||||
return this.lambdaQuery().in(Dept::getCode, list).eq(Dept::getState, DataStateEnum.ENABLE.getCode()).list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Dept> getDeptInfoListByIds(List<String> deptIds) {
|
||||
return this.lambdaQuery().in(Dept::getId,deptIds).eq(Dept::getState,DataStateEnum.ENABLE.getCode()).list();
|
||||
return this.lambdaQuery().in(Dept::getId, deptIds).eq(Dept::getState, DataStateEnum.ENABLE.getCode()).list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Dept> getAllDept() {
|
||||
return this.lambdaQuery().eq(Dept::getState,DataStateEnum.ENABLE.getCode()).list();
|
||||
return this.lambdaQuery().eq(Dept::getState, DataStateEnum.ENABLE.getCode()).list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tree<String>> orgTreeSelector() {
|
||||
List<Dept> deptList = this.lambdaQuery().eq(Dept::getState, DataStateEnum.ENABLE.getCode()).list();
|
||||
List<TreeNode<String>> treeNodeList = deptList.stream().map(dept ->
|
||||
new TreeNode<>(dept.getId(), dept.getPid(), dept.getName(), dept.getSort()))
|
||||
.collect(Collectors.toList());
|
||||
return TreeUtil.build(treeNodeList, "0");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -204,6 +204,20 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IR
|
||||
return this.lambdaQuery().eq(Role::getCode,code).eq(Role::getState,1).one();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Role> allRoleList() {
|
||||
QueryWrapper<Role> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.ne("sys_role.state", DataStateEnum.DELETED.getCode());
|
||||
return this.baseMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Role> simpleList() {
|
||||
LambdaQueryWrapper<Role> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.select(Role::getId,Role::getName).eq(Role::getState,DataStateEnum.ENABLE.getCode());
|
||||
return this.baseMapper.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验参数,检查是否存在相同编码的角色代码
|
||||
*/
|
||||
|
||||
@@ -100,18 +100,18 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
|
||||
}
|
||||
List<String> roleNames = roleService.getRoleNameByUserId(user.getId());
|
||||
UserSet userSet = userSetService.lambdaQuery().eq(UserSet::getUserId, user.getId()).one();
|
||||
return new UserDTO(user.getId(), user.getLoginName(), user.getName(), user.getPassword(), roleNames, userSet.getSecretKey(), userSet.getStandBy(), user.getDeptId(), user.getType(),user.getHeadSculpture());
|
||||
return new UserDTO(user.getId(), user.getLoginName(), user.getName(), user.getPassword(), roleNames, userSet.getSecretKey(), userSet.getStandBy(), user.getDeptId(), user.getType(), user.getHeadSculpture());
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDTO loadUserByPhone(String phone) {
|
||||
User user = getUserByPhone(phone,false,null);
|
||||
User user = getUserByPhone(phone, false, null);
|
||||
if (Objects.isNull(user)) {
|
||||
return null;
|
||||
}
|
||||
List<String> roleNames = roleService.getRoleNameByUserId(user.getId());
|
||||
UserSet userSet = userSetService.lambdaQuery().eq(UserSet::getUserId, user.getId()).one();
|
||||
return new UserDTO(user.getId(), user.getLoginName(), user.getName(), user.getPassword(), roleNames, userSet.getSecretKey(), userSet.getStandBy(), user.getDeptId(), user.getType(),user.getHeadSculpture());
|
||||
return new UserDTO(user.getId(), user.getLoginName(), user.getName(), user.getPassword(), roleNames, userSet.getSecretKey(), userSet.getStandBy(), user.getDeptId(), user.getType(), user.getHeadSculpture());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -256,10 +256,10 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
|
||||
return null;
|
||||
}
|
||||
BeanUtil.copyProperties(user, userVO);
|
||||
if (!Objects.isNull(user.getDeptId())){
|
||||
if (!Objects.isNull(user.getDeptId())) {
|
||||
Dept dept = deptService.getDeptById(user.getDeptId());
|
||||
//非自定义部门
|
||||
if (Objects.equals(dept.getType(),0)){
|
||||
if (Objects.equals(dept.getType(), 0)) {
|
||||
String areaId = deptService.getAreaIdByDeptId(user.getDeptId());
|
||||
userVO.setAreaId(areaId);
|
||||
userVO.setAreaName(areaFeignClient.selectIdArea(areaId).getData().getName());
|
||||
@@ -285,12 +285,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
|
||||
if (Objects.equals(UserType.SUPER_ADMINISTRATOR, type)) {
|
||||
type = UserType.ADMINISTRATOR;
|
||||
//fixme 存在web用户和App用户,目前先通过管理员的登录名来区分开
|
||||
} else if (Objects.equals(UserType.ADMINISTRATOR, type) && !Objects.equals(user.getLoginName(),"njcnyw")) {
|
||||
} else if (Objects.equals(UserType.ADMINISTRATOR, type) && !Objects.equals(user.getLoginName(), "njcnyw")) {
|
||||
type = UserType.USER;
|
||||
} else if (Objects.equals(UserType.ADMINISTRATOR, type) && Objects.equals(user.getLoginName(),"njcnyw")) {
|
||||
} else if (Objects.equals(UserType.ADMINISTRATOR, type) && Objects.equals(user.getLoginName(), "njcnyw")) {
|
||||
type = UserType.APP;
|
||||
}
|
||||
else if (Objects.equals(UserType.USER, type) || Objects.equals(UserType.APP, type)) {
|
||||
} else if (Objects.equals(UserType.USER, type) || Objects.equals(UserType.APP, type)) {
|
||||
return page;
|
||||
}
|
||||
if (ObjectUtil.isNotNull(queryParam)) {
|
||||
@@ -356,7 +355,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
|
||||
|
||||
@Override
|
||||
public boolean updatePassword(String id, String password) {
|
||||
String secretPassword = userSetService.updatePassword(id, password,true);
|
||||
String secretPassword = userSetService.updatePassword(id, password, true);
|
||||
User user = lambdaQuery().eq(User::getId, id).one();
|
||||
user.setPassword(secretPassword);
|
||||
user.setPwdValidity(LocalDateTime.now());
|
||||
@@ -431,7 +430,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
|
||||
String fileName = methodDescribe + ".xlsx";
|
||||
String targetDir = generalInfo.getBusinessTempPath() + File.separator + RequestUtil.getUserIndex();
|
||||
File parentDir = new File(targetDir);
|
||||
if(!parentDir.exists()){
|
||||
if (!parentDir.exists()) {
|
||||
parentDir.mkdirs();
|
||||
}
|
||||
File excel = new File(targetDir, fileName);
|
||||
@@ -488,14 +487,14 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
|
||||
@Override
|
||||
public List<User> getUserListByDeptId(String deptId) {
|
||||
LambdaQueryWrapper<Dept> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.like(Dept::getPids,deptId).select(Dept::getId);
|
||||
lambdaQueryWrapper.like(Dept::getPids, deptId).select(Dept::getId);
|
||||
List<String> deptIds = new ArrayList<>();
|
||||
deptIds.add(deptId);
|
||||
List<Dept> deptList = deptService.list(lambdaQueryWrapper);
|
||||
if(CollectionUtil.isNotEmpty(deptIds)){
|
||||
if (CollectionUtil.isNotEmpty(deptIds)) {
|
||||
deptIds.addAll(deptList.stream().map(Dept::getId).distinct().collect(Collectors.toList()));
|
||||
}
|
||||
return this.list(new LambdaQueryWrapper<User>().in(User::getDeptId,deptIds).select(User::getId,User::getName,User::getLoginName));
|
||||
return this.list(new LambdaQueryWrapper<User>().in(User::getDeptId, deptIds).select(User::getId, User::getName, User::getLoginName));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -528,6 +527,42 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
|
||||
return userRoleMapper.getUserByRoleType(roleType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserVO> listAllUserByDeptId(String deptId) {
|
||||
//查询所有状态正常的用户信息,目前只要id和name
|
||||
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
if (StrUtil.isNotBlank(deptId)) {
|
||||
lambdaQueryWrapper.eq(User::getDeptId, deptId);
|
||||
}
|
||||
lambdaQueryWrapper.eq(User::getState, DataStateEnum.ENABLE.getCode())
|
||||
.select(User::getName,User::getId);
|
||||
List<User> users = this.baseMapper.selectList(lambdaQueryWrapper);
|
||||
if (CollectionUtil.isEmpty(users)) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
return BeanUtil.copyToList(users, UserVO.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUserIdByRoleId(List<String> roleId) {
|
||||
LambdaQueryWrapper<UserRole> userRoleLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
userRoleLambdaQueryWrapper.in(UserRole::getRoleId,roleId);
|
||||
List<UserRole> userRole = userRoleService.list(userRoleLambdaQueryWrapper);
|
||||
if(CollectionUtil.isEmpty(userRole)){
|
||||
return new ArrayList<>();
|
||||
}else{
|
||||
return userRole.stream().map(UserRole::getUserId).distinct().collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<User> simpleList() {
|
||||
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
userLambdaQueryWrapper.select(User::getId,User::getName).eq(User::getState,DataStateEnum.ENABLE.getCode());
|
||||
return this.baseMapper.selectList(userLambdaQueryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据登录名查询用户
|
||||
*
|
||||
@@ -576,8 +611,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
|
||||
*/
|
||||
private void judgeIp(@NotNull User user, UserStrategy userStrategy) {
|
||||
String ipSection = user.getLimitIpStart() + "-" + user.getLimitIpEnd();
|
||||
log.error("用户实际ip:"+RequestUtil.getRealIp());
|
||||
log.error("用户限制ip:"+ipSection);
|
||||
log.error("用户实际ip:" + RequestUtil.getRealIp());
|
||||
log.error("用户限制ip:" + ipSection);
|
||||
if (RequestUtil.getRealIp().equalsIgnoreCase(LogInfo.UNKNOWN_IP)) {
|
||||
//feign接口可能获取的IP是空的
|
||||
throw new BusinessException(UserResponseEnum.INVALID_IP);
|
||||
|
||||
Reference in New Issue
Block a user