# Conflicts:
#	yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnum.java
#	yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumCollectionValidator.java
#	yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumValidator.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessListenerServiceImpl.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageUserController.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
#	yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java
pull/173/head
YunaiV 2025-01-25 10:14:47 +08:00
commit 8523bdfbab
162 changed files with 2901 additions and 748 deletions

View File

@ -0,0 +1,15 @@
package cn.iocoder.yudao.framework.common.core;
/**
* T
*
* @author HUIHUI
*/
public interface ArrayValuable<T> {
/**
* @return
*/
T[] array();
}

View File

@ -1,15 +0,0 @@
package cn.iocoder.yudao.framework.common.core;
/**
* Int
*
* @author
*/
public interface IntArrayValuable {
/**
* @return int
*/
int[] array();
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,12 +14,12 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum CommonStatusEnum implements IntArrayValuable {
public enum CommonStatusEnum implements ArrayValuable<Integer> {
ENABLE(0, "开启"),
DISABLE(1, "关闭");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CommonStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CommonStatusEnum::getStatus).toArray(Integer[]::new);
/**
*
@ -31,7 +31,7 @@ public enum CommonStatusEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,7 +14,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum DateIntervalEnum implements IntArrayValuable {
public enum DateIntervalEnum implements ArrayValuable<Integer> {
DAY(1, "天"),
WEEK(2, "周"),
@ -23,7 +23,7 @@ public enum DateIntervalEnum implements IntArrayValuable {
YEAR(5, "年")
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getInterval).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DateIntervalEnum::getInterval).toArray(Integer[]::new);
/**
*
@ -35,7 +35,7 @@ public enum DateIntervalEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -14,7 +14,7 @@ public enum DocumentEnum {
REDIS_INSTALL("https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues/I4VCSJ", "Redis 安装文档"),
TENANT("https://doc.iocoder.cn", "SaaS 多租户文档");
private final String url;
private final String memo;

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@RequiredArgsConstructor
@Getter
public enum TerminalEnum implements IntArrayValuable {
public enum TerminalEnum implements ArrayValuable<Integer> {
UNKNOWN(0, "未知"), // 目的:在无法解析到 terminal 时,使用它
WECHAT_MINI_PROGRAM(10, "微信小程序"),
@ -22,7 +22,7 @@ public enum TerminalEnum implements IntArrayValuable {
APP(31, "手机 App"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TerminalEnum::getTerminal).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(TerminalEnum::getTerminal).toArray(Integer[]::new);
/**
*
@ -34,7 +34,7 @@ public enum TerminalEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -12,12 +12,12 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum UserTypeEnum implements IntArrayValuable {
public enum UserTypeEnum implements ArrayValuable<Integer> {
MEMBER(1, "会员"), // 面向 c 端,普通用户
ADMIN(2, "管理员"); // 面向 b 端,管理后台
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(UserTypeEnum::getValue).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(UserTypeEnum::getValue).toArray(Integer[]::new);
/**
*
@ -33,7 +33,7 @@ public enum UserTypeEnum implements IntArrayValuable {
}
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -1,9 +1,9 @@
package cn.iocoder.yudao.framework.common.validation;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({
@ -22,9 +22,9 @@ import java.lang.annotation.*;
public @interface InEnum {
/**
* @return EnumValuable
* @return ArrayValuable
*/
Class<? extends IntArrayValuable> value();
Class<? extends ArrayValuable<?>> value();
String message() default "必须在指定范围 {value}";

View File

@ -1,37 +1,39 @@
package cn.iocoder.yudao.framework.common.validation;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<Integer>> {
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<?>> {
private List<Integer> values;
private List<?> values;
@Override
public void initialize(InEnum annotation) {
IntArrayValuable[] values = annotation.value().getEnumConstants();
ArrayValuable<?>[] values = annotation.value().getEnumConstants();
if (values.length == 0) {
this.values = Collections.emptyList();
} else {
this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
this.values = Arrays.asList(values[0].array());
}
}
@Override
public boolean isValid(Collection<Integer> list, ConstraintValidatorContext context) {
public boolean isValid(Collection<?> list, ConstraintValidatorContext context) {
if (list == null) {
return true;
}
// 校验通过
if (CollUtil.containsAll(values, list)) {
return true;
}
// 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
// 校验不通过,自定义提示语句
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
.replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句

View File

@ -1,30 +1,29 @@
package cn.iocoder.yudao.framework.common.validation;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {
public class InEnumValidator implements ConstraintValidator<InEnum, Object> {
private List<Integer> values;
private List<?> values;
@Override
public void initialize(InEnum annotation) {
IntArrayValuable[] values = annotation.value().getEnumConstants();
ArrayValuable<?>[] values = annotation.value().getEnumConstants();
if (values.length == 0) {
this.values = Collections.emptyList();
} else {
this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
this.values = Arrays.asList(values[0].array());
}
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
public boolean isValid(Object value, ConstraintValidatorContext context) {
// 为空时,默认不校验,即认为通过
if (value == null) {
return true;
@ -33,7 +32,7 @@ public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {
if (values.contains(value)) {
return true;
}
// 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
// 校验不通过,自定义提示语句
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
.replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.ip.core.enums;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum AreaTypeEnum implements IntArrayValuable {
public enum AreaTypeEnum implements ArrayValuable<Integer> {
COUNTRY(1, "国家"),
PROVINCE(2, "省份"),
@ -21,7 +21,7 @@ public enum AreaTypeEnum implements IntArrayValuable {
DISTRICT(4, "地区"), // 县、镇、区等
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AreaTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AreaTypeEnum::getType).toArray(Integer[]::new);
/**
*
@ -33,7 +33,7 @@ public enum AreaTypeEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -127,17 +127,17 @@ public class YudaoWebSecurityConfigurerAdapter {
httpSecurity
// ①:全局共享规则
.authorizeHttpRequests(c -> c
// 1.1 静态资源,可匿名访问
.requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll()
// 1.2 设置 @PermitAll 无需认证
.requestMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.PUT, permitAllUrls.get(HttpMethod.PUT).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.HEAD, permitAllUrls.get(HttpMethod.HEAD).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.PATCH, permitAllUrls.get(HttpMethod.PATCH).toArray(new String[0])).permitAll()
// 1.3 基于 yudao.security.permit-all-urls 无需认证
.requestMatchers(securityProperties.getPermitAllUrls().toArray(new String[0])).permitAll()
// 1.1 静态资源,可匿名访问
.requestMatchers(HttpMethod.GET, "/*.html", "/*.css", "/*.js").permitAll()
// 1.2 设置 @PermitAll 无需认证
.requestMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.PUT, permitAllUrls.get(HttpMethod.PUT).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.HEAD, permitAllUrls.get(HttpMethod.HEAD).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.PATCH, permitAllUrls.get(HttpMethod.PATCH).toArray(new String[0])).permitAll()
// 1.3 基于 yudao.security.permit-all-urls 无需认证
.requestMatchers(securityProperties.getPermitAllUrls().toArray(new String[0])).permitAll()
)
// ②:每个项目的自定义规则
.authorizeHttpRequests(c -> authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(c)))

View File

@ -146,9 +146,11 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
if (handlerMethod != null) {
Tag tagAnnotation = handlerMethod.getBeanType().getAnnotation(Tag.class);
Operation operationAnnotation = handlerMethod.getMethodAnnotation(Operation.class);
String operateModule = accessLogAnnotation != null ? accessLogAnnotation.operateModule() :
String operateModule = accessLogAnnotation != null && StrUtil.isNotBlank(accessLogAnnotation.operateModule()) ?
accessLogAnnotation.operateModule() :
tagAnnotation != null ? StrUtil.nullToDefault(tagAnnotation.name(), tagAnnotation.description()) : null;
String operateName = accessLogAnnotation != null ? accessLogAnnotation.operateName() :
String operateName = accessLogAnnotation != null && StrUtil.isNotBlank(accessLogAnnotation.operateName()) ?
accessLogAnnotation.operateName() :
operationAnnotation != null ? operationAnnotation.summary() : null;
OperateTypeEnum operateType = accessLogAnnotation != null && accessLogAnnotation.operateType().length > 0 ?
accessLogAnnotation.operateType()[0] : parseOperateLogType(request);

View File

@ -26,19 +26,14 @@ public abstract class AbstractSliderDesensitizationHandler<T extends Annotation>
int suffixKeep = getSuffixKeep(annotation);
String replacer = getReplacer(annotation);
int length = origin.length();
// 情况一:原始字符串长度小于等于保留长度,则原始字符串全部替换
if (prefixKeep >= length || suffixKeep >= length) {
return buildReplacerByLength(replacer, length);
}
// 情况二:原始字符串长度小于等于前后缀保留字符串长度,则原始字符串全部替换
if ((prefixKeep + suffixKeep) >= length) {
return buildReplacerByLength(replacer, length);
}
// 情况三:原始字符串长度大于前后缀保留字符串长度,则替换中间字符串
int interval = length - prefixKeep - suffixKeep;
// 情况一:原始字符串长度小于等于前后缀保留字符串长度,则原始字符串全部替换
if (interval <= 0) {
return buildReplacerByLength(replacer, length);
}
// 情况二:原始字符串长度大于前后缀保留字符串长度,则替换中间字符串
return origin.substring(0, prefixKeep) +
buildReplacerByLength(replacer, interval) +
origin.substring(prefixKeep + interval);
@ -52,11 +47,7 @@ public abstract class AbstractSliderDesensitizationHandler<T extends Annotation>
* @return
*/
private String buildReplacerByLength(String replacer, int length) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append(replacer);
}
return builder.toString();
return replacer.repeat(length);
}
/**

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.ai.enums.knowledge;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum AiKnowledgeDocumentStatusEnum implements IntArrayValuable {
public enum AiKnowledgeDocumentStatusEnum implements ArrayValuable<Integer> {
IN_PROGRESS(10, "索引中"),
SUCCESS(20, "可用"),
@ -29,10 +29,10 @@ public enum AiKnowledgeDocumentStatusEnum implements IntArrayValuable {
*/
private final String name;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiKnowledgeDocumentStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiKnowledgeDocumentStatusEnum::getStatus).toArray(Integer[]::new);
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.ai.enums.music;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum AiMusicGenerateModeEnum implements IntArrayValuable {
public enum AiMusicGenerateModeEnum implements ArrayValuable<Integer> {
DESCRIPTION(1, "描述模式"),
LYRIC(2, "歌词模式");
@ -27,10 +27,10 @@ public enum AiMusicGenerateModeEnum implements IntArrayValuable {
*/
private final String name;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiMusicGenerateModeEnum::getMode).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiMusicGenerateModeEnum::getMode).toArray(Integer[]::new);
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.ai.enums.music;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum AiMusicStatusEnum implements IntArrayValuable {
public enum AiMusicStatusEnum implements ArrayValuable<Integer> {
IN_PROGRESS(10, "进行中"),
SUCCESS(20, "已完成"),
@ -29,10 +29,10 @@ public enum AiMusicStatusEnum implements IntArrayValuable {
*/
private final String name;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiMusicStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiMusicStatusEnum::getStatus).toArray(Integer[]::new);
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.ai.enums.write;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum AiWriteTypeEnum implements IntArrayValuable {
public enum AiWriteTypeEnum implements ArrayValuable<Integer> {
WRITING(1, "撰写", "请撰写一篇关于 [{}] 的文章。文章的内容格式:{},语气:{},语言:{},长度:{}。请确保涵盖主要内容,不需要除了正文内容外的其他回复,如标题、额外的解释或道歉。"),
REPLY(2, "回复", "请针对如下内容:[{}] 做个回复。回复内容参考:[{}], 回复格式:{},语气:{},语言:{},长度:{}。不需要除了正文内容外的其他回复,如标题、开头、额外的解释或道歉。");
@ -32,10 +32,10 @@ public enum AiWriteTypeEnum implements IntArrayValuable {
*/
private final String prompt;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiWriteTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiWriteTypeEnum::getType).toArray(Integer[]::new);
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -38,6 +38,7 @@ public interface ErrorCodeConstants {
ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, "任务({})的候选人未配置");
ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在");
ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程");
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消");
// ========== 流程任务 1-009-005-000 ==========
ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你");
@ -54,6 +55,8 @@ public interface ErrorCodeConstants {
ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人");
ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在");
ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!");
ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!");
// ========== 动态表单模块 1-009-010-000 ==========
ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在");

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* BPM
*
* @author Lesan
*/
@Getter
@AllArgsConstructor
public enum BpmAutoApproveTypeEnum implements ArrayValuable<Integer> {
NONE(0, "不自动通过"),
APPROVE_ALL(1, "仅审批一次,后续重复的审批节点均自动通过"),
APPROVE_SEQUENT(2, "仅针对连续审批的节点自动通过");
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmAutoApproveTypeEnum::getType).toArray(Integer[]::new);
private final Integer type;
private final String name;
@Override
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -11,14 +11,15 @@ import lombok.Getter;
*/
@Getter
@AllArgsConstructor
public enum BpmBoundaryEventType {
public enum BpmBoundaryEventTypeEnum {
USER_TASK_TIMEOUT(1,"用户任务超时");
USER_TASK_TIMEOUT(1, "用户任务超时"),
DELAY_TIMER_TIMEOUT(2, "延迟器超时");
private final Integer type;
private final String name;
public static BpmBoundaryEventType typeOf(Integer type) {
public static BpmBoundaryEventTypeEnum typeOf(Integer type) {
return ArrayUtil.firstMatch(eventType -> eventType.getType().equals(type), values());
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* BPM
*
* @author Lesan
*/
@Getter
@AllArgsConstructor
public enum BpmDelayTimerTypeEnum implements ArrayValuable<Integer> {
FIXED_TIME_DURATION(1, "固定时长"),
FIXED_DATE_TIME(2, "固定日期");
private final Integer type;
private final String name;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmDelayTimerTypeEnum::getType).toArray(Integer[]::new);
@Override
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* BPM HTTP Simple
*
* @author Lesan
*/
@Getter
@AllArgsConstructor
public enum BpmHttpRequestParamTypeEnum implements ArrayValuable<Integer> {
FIXED_VALUE(1, "固定值"),
FROM_FORM(2, "表单");
private final Integer type;
private final String name;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmHttpRequestParamTypeEnum::getType).toArray(Integer[]::new);
@Override
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,19 +13,19 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum BpmModelFormTypeEnum implements IntArrayValuable {
public enum BpmModelFormTypeEnum implements ArrayValuable<Integer> {
NORMAL(10, "流程表单"), // 对应 BpmFormDO
CUSTOM(20, "业务表单") // 业务自己定义的表单,自己进行数据的存储
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmModelFormTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmModelFormTypeEnum::getType).toArray(Integer[]::new);
private final Integer type;
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,18 +13,18 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum BpmModelTypeEnum implements IntArrayValuable {
public enum BpmModelTypeEnum implements ArrayValuable<Integer> {
BPMN(10, "BPMN 设计器"), // https://bpmn.io/toolkit/bpmn-js/
SIMPLE(20, "SIMPLE 设计器"); // 参考钉钉、飞书工作流的设计器
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmModelTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmModelTypeEnum::getType).toArray(Integer[]::new);
private final Integer type;
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -10,7 +10,7 @@ import lombok.Getter;
*/
@Getter
@AllArgsConstructor
public enum BpmProcessListenerType {
public enum BpmProcessListenerTypeEnum {
EXECUTION("execution", "执行监听器"),
TASK("task", "任务执行器");

View File

@ -10,7 +10,7 @@ import lombok.Getter;
*/
@Getter
@AllArgsConstructor
public enum BpmProcessListenerValueType {
public enum BpmProcessListenerValueTypeEnum {
CLASS("class", "Java 类"),
DELEGATE_EXPRESSION("delegateExpression", "代理表达式"),

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,23 +14,23 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum BpmSimpleModeConditionType implements IntArrayValuable {
public enum BpmSimpleModeConditionTypeEnum implements ArrayValuable<Integer> {
EXPRESSION(1, "条件表达式"),
RULE(2, "条件规则");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModeConditionType::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmSimpleModeConditionTypeEnum::getType).toArray(Integer[]::new);
private final Integer type;
private final String name;
public static BpmSimpleModeConditionType valueOf(Integer type) {
public static BpmSimpleModeConditionTypeEnum valueOf(Integer type) {
return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values());
}
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -15,7 +15,7 @@ import java.util.Objects;
*/
@Getter
@AllArgsConstructor
public enum BpmSimpleModelNodeType implements IntArrayValuable {
public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable<Integer> {
// 0 ~ 1 开始和结束
START_NODE(0, "开始", "startEvent"),
@ -26,14 +26,18 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable {
APPROVE_NODE(11, "审批人", "userTask"),
COPY_NODE(12, "抄送人", "serviceTask"),
DELAY_TIMER_NODE(14, "延迟器", "receiveTask"),
TRIGGER_NODE(15, "触发器", "serviceTask"),
// 50 ~ 条件分支
CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式
CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"),
PARALLEL_BRANCH_NODE(52, "并行分支", "parallelGateway"),
INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"),
ROUTER_BRANCH_NODE(54, "路由分支", "exclusiveGateway")
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModelNodeType::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmSimpleModelNodeTypeEnum::getType).toArray(Integer[]::new);
private final Integer type;
private final String name;
@ -47,15 +51,16 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable {
public static boolean isBranchNode(Integer type) {
return Objects.equals(CONDITION_BRANCH_NODE.getType(), type)
|| Objects.equals(PARALLEL_BRANCH_NODE.getType(), type)
|| Objects.equals(INCLUSIVE_BRANCH_NODE.getType(), type);
|| Objects.equals(INCLUSIVE_BRANCH_NODE.getType(), type)
|| Objects.equals(ROUTER_BRANCH_NODE.getType(), type);
}
public static BpmSimpleModelNodeType valueOf(Integer type) {
public static BpmSimpleModelNodeTypeEnum valueOf(Integer type) {
return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values());
}
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* BPM Simple
*
* @author jason
*/
@Getter
@AllArgsConstructor
public enum BpmTriggerTypeEnum implements ArrayValuable<Integer> {
HTTP_REQUEST(1, "发起 HTTP 请求");
/**
*
*/
private final Integer type;
/**
*
*/
private final String desc;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmTriggerTypeEnum::getType).toArray(Integer[]::new);
@Override
public Integer[] array() {
return ARRAYS;
}
public static BpmTriggerTypeEnum typeOf(Integer type) {
return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
}
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,7 +14,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum BpmUserTaskApproveMethodEnum implements IntArrayValuable {
public enum BpmUserTaskApproveMethodEnum implements ArrayValuable<Integer> {
RANDOM(1, "随机挑选一人审批", null),
RATIO(2, "多人会签(按通过比例)", "${ nrOfCompletedInstances/nrOfInstances >= %s}"), // 会签(按通过比例)
@ -34,14 +34,14 @@ public enum BpmUserTaskApproveMethodEnum implements IntArrayValuable {
*/
private final String completionCondition;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskApproveMethodEnum::getMethod).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskApproveMethodEnum::getMethod).toArray(Integer[]::new);
public static BpmUserTaskApproveMethodEnum valueOf(Integer method) {
return ArrayUtil.firstMatch(item -> item.getMethod().equals(method), values());
}
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,18 +13,18 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum BpmUserTaskApproveTypeEnum implements IntArrayValuable {
public enum BpmUserTaskApproveTypeEnum implements ArrayValuable<Integer> {
USER(1), // 人工审批
AUTO_APPROVE(2), // 自动通过
AUTO_REJECT(3); // 自动拒绝
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskApproveTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskApproveTypeEnum::getType).toArray(Integer[]::new);
private final Integer type;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@RequiredArgsConstructor
@Getter
public enum BpmUserTaskAssignEmptyHandlerTypeEnum implements IntArrayValuable {
public enum BpmUserTaskAssignEmptyHandlerTypeEnum implements ArrayValuable<Integer> {
APPROVE(1), // 自动通过
REJECT(2), // 自动拒绝
@ -21,12 +21,12 @@ public enum BpmUserTaskAssignEmptyHandlerTypeEnum implements IntArrayValuable {
ASSIGN_ADMIN(4), // 转交给流程管理员
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskAssignEmptyHandlerTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskAssignEmptyHandlerTypeEnum::getType).toArray(Integer[]::new);
private final Integer type;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -13,18 +13,18 @@ import java.util.Arrays;
*/
@RequiredArgsConstructor
@Getter
public enum BpmUserTaskAssignStartUserHandlerTypeEnum implements IntArrayValuable {
public enum BpmUserTaskAssignStartUserHandlerTypeEnum implements ArrayValuable<Integer> {
START_USER_AUDIT(1), // 由发起人对自己审批
SKIP(2), // 自动跳过【参考飞书】1如果当前节点还有其他审批人则交由其他审批人进行审批2如果当前节点没有其他审批人则该节点自动通过
TRANSFER_DEPT_LEADER(3); // 转交给部门负责人审批【参考飞书】:若部门负责人为空,则自动通过
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskAssignStartUserHandlerTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskAssignStartUserHandlerTypeEnum::getType).toArray(Integer[]::new);
private final Integer type;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,7 +14,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum BpmUserTaskRejectHandlerType implements IntArrayValuable {
public enum BpmUserTaskRejectHandlerTypeEnum implements ArrayValuable<Integer> {
FINISH_PROCESS_INSTANCE(1, "终止流程"),
RETURN_USER_TASK(2, "驳回到指定任务节点");
@ -22,14 +22,14 @@ public enum BpmUserTaskRejectHandlerType implements IntArrayValuable {
private final Integer type;
private final String name;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskRejectHandlerType::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskRejectHandlerTypeEnum::getType).toArray(Integer[]::new);
public static BpmUserTaskRejectHandlerType typeOf(Integer type) {
public static BpmUserTaskRejectHandlerTypeEnum typeOf(Integer type) {
return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
}
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum BpmUserTaskTimeoutHandlerTypeEnum implements IntArrayValuable {
public enum BpmUserTaskTimeoutHandlerTypeEnum implements ArrayValuable<Integer> {
REMINDER(1,"自动提醒"),
APPROVE(2, "自动同意"),
@ -22,10 +22,10 @@ public enum BpmUserTaskTimeoutHandlerTypeEnum implements IntArrayValuable {
private final Integer type;
private final String name;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskTimeoutHandlerTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskTimeoutHandlerTypeEnum::getType).toArray(Integer[]::new);
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.bpm.enums.task;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,7 +14,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum BpmProcessInstanceStatusEnum implements IntArrayValuable {
public enum BpmProcessInstanceStatusEnum implements ArrayValuable<Integer> {
NOT_START(-1, "未开始"),
RUNNING(1, "审批中"),
@ -22,7 +22,7 @@ public enum BpmProcessInstanceStatusEnum implements IntArrayValuable {
REJECT(3, "审批不通过"),
CANCEL(4, "已取消");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmProcessInstanceStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmProcessInstanceStatusEnum::getStatus).toArray(Integer[]::new);
/**
*
@ -34,7 +34,7 @@ public enum BpmProcessInstanceStatusEnum implements IntArrayValuable {
private final String desc;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -161,6 +161,15 @@ public class BpmModelController {
return success(true);
}
@DeleteMapping("/clean")
@Operation(summary = "清理模型")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:model:clean')")
public CommonResult<Boolean> cleanModel(@RequestParam("id") String id) {
modelService.cleanModel(getLoginUserId(), id);
return success(true);
}
// ========== 仿钉钉/飞书的精简模型 =========
@GetMapping("/simple/get")

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form;
import lombok.Data;
/**
* VO
*/
@Data
public class BpmFormFieldVO {
/**
*
*/
private String type;
/**
*
*/
private String field;
/**
*
*/
private String title;
}

View File

@ -1,14 +1,16 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
@ -62,4 +64,72 @@ public class BpmModelMetaInfoVO {
@Schema(description = "排序", example = "1")
private Long sort; // 创建时,后端自动生成
@Schema(description = "允许撤销审批中的申请", example = "true")
private Boolean allowCancelRunningProcess;
@Schema(description = "流程 ID 规则", example = "{}")
private ProcessIdRule processIdRule;
@Schema(description = "自动去重类型", example = "1")
@InEnum(BpmAutoApproveTypeEnum.class)
private Integer autoApprovalType;
@Schema(description = "标题设置", example = "{}")
private TitleSetting titleSetting;
@Schema(description = "摘要设置", example = "{}")
private SummarySetting summarySetting;
@Schema(description = "流程 ID 规则")
@Data
@Valid
public static class ProcessIdRule {
@Schema(description = "是否启用", example = "false")
@NotNull(message = "是否启用不能为空")
private Boolean enable;
@Schema(description = "前缀", example = "XX")
private String prefix;
@Schema(description = "中缀", example = "20250120")
private String infix; // 精确到日、精确到时、精确到分、精确到秒
@Schema(description = "后缀", example = "YY")
private String postfix;
@Schema(description = "序列长度", example = "5")
@NotNull(message = "序列长度不能为空")
private Integer length;
}
@Schema(description = "标题设置")
@Data
@Valid
public static class TitleSetting {
@Schema(description = "是否自定义", example = "false")
@NotNull(message = "是否自定义不能为空")
private Boolean enable;
@Schema(description = "标题", example = "流程标题")
private String title;
}
@Schema(description = "摘要设置")
@Data
@Valid
public static class SummarySetting {
@Schema(description = "是否自定义", example = "false")
@NotNull(message = "是否自定义不能为空")
private Boolean enable;
@Schema(description = "摘要字段数组", example = "[]")
private List<String> summary;
}
}

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidat
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
@ -24,7 +25,7 @@ public class BpmSimpleModelNodeVO {
@Schema(description = "模型节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "模型节点类型不能为空")
@InEnum(BpmSimpleModelNodeType.class)
@InEnum(BpmSimpleModelNodeTypeEnum.class)
private Integer type;
@Schema(description = "模型节点名称", example = "领导审批")
@ -36,23 +37,6 @@ public class BpmSimpleModelNodeVO {
@Schema(description = "子节点")
private BpmSimpleModelNodeVO childNode; // 补充说明:在该模型下,子节点有且仅有一个,不会有多个
@Schema(description = "条件节点")
private List<BpmSimpleModelNodeVO> conditionNodes; // 补充说明:有且仅有条件、并行、包容等分支会使用
@Schema(description = "条件类型", example = "1")
@InEnum(BpmSimpleModeConditionType.class)
private Integer conditionType; // 仅用于条件节点 BpmSimpleModelNodeType.CONDITION_NODE
@Schema(description = "条件表达式", example = "${day>3}")
private String conditionExpression; // 仅用于条件节点 BpmSimpleModelNodeType.CONDITION_NODE
@Schema(description = "是否默认条件", example = "true")
private Boolean defaultFlow; // 仅用于条件节点 BpmSimpleModelNodeType.CONDITION_NODE
/**
*
*/
private ConditionGroups conditionGroups; // 仅用于条件节点 BpmSimpleModelNodeType.CONDITION_NODE
@Schema(description = "候选人策略", example = "30")
@InEnum(BpmTaskCandidateStrategyEnum.class)
private Integer candidateStrategy; // 用于审批,抄送节点
@ -77,6 +61,12 @@ public class BpmSimpleModelNodeVO {
@Schema(description = "操作按钮设置", example = "[]")
private List<OperationButtonSetting> buttonsSetting; // 用于审批节点
@Schema(description = "是否需要签名", example = "false")
private Boolean signEnable;
@Schema(description = "是否填写审批意见", example = "false")
private Boolean reasonRequire;
/**
*
*/
@ -96,12 +86,85 @@ public class BpmSimpleModelNodeVO {
*/
private AssignEmptyHandler assignEmptyHandler;
/**
*
*/
private ListenerHandler taskCreateListener;
/**
*
*/
private ListenerHandler taskAssignListener;
/**
*
*/
private ListenerHandler taskCompleteListener;
@Schema(description = "延迟器设置", example = "{}")
private DelaySetting delaySetting;
@Schema(description = "条件节点")
private List<BpmSimpleModelNodeVO> conditionNodes; // 补充说明:有且仅有条件、并行、包容分支会使用
/**
*
*/
private ConditionSetting conditionSetting; // 仅用于条件节点 BpmSimpleModelNodeType.CONDITION_NODE
@Schema(description = "路由分支组", example = "[]")
private List<RouterSetting> routerGroups;
@Schema(description = "路由分支默认分支 ID", example = "Flow_xxx", hidden = true) // 由后端生成,所以 hidden = true
private String routerDefaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTER_BRANCH_NODE
/**
*
*/
private TriggerSetting triggerSetting;
@Schema(description = "任务监听器")
@Valid
@Data
public static class ListenerHandler {
@Schema(description = "是否开启任务监听器", example = "false")
@NotNull(message = "是否开启任务监听器不能为空")
private Boolean enable;
@Schema(description = "请求路径", example = "http://xxxxx")
private String path;
@Schema(description = "请求头", example = "[]")
private List<HttpRequestParam> header;
@Schema(description = "请求体", example = "[]")
private List<HttpRequestParam> body;
}
@Schema(description = "HTTP 请求参数设置")
@Data
public static class HttpRequestParam {
@Schema(description = "值类型", example = "1")
@InEnum(BpmHttpRequestParamTypeEnum.class)
@NotNull(message = "值类型不能为空")
private Integer type;
@Schema(description = "键", example = "xxx")
@NotEmpty(message = "键不能为空")
private String key;
@Schema(description = "值", example = "xxx")
@NotEmpty(message = "值不能为空")
private String value;
}
@Schema(description = "审批节点拒绝处理策略")
@Data
public static class RejectHandler {
@Schema(description = "拒绝处理类型", example = "1")
@InEnum(BpmUserTaskRejectHandlerType.class)
@InEnum(BpmUserTaskRejectHandlerTypeEnum.class)
private Integer type;
@Schema(description = "任务拒绝后驳回的节点 Id", example = "Activity_1")
@ -128,7 +191,6 @@ public class BpmSimpleModelNodeVO {
@Schema(description = "最大提醒次数", example = "1")
private Integer maxRemindCount;
}
@Schema(description = "空处理策略")
@ -143,7 +205,6 @@ public class BpmSimpleModelNodeVO {
@Schema(description = "指定人员审批的用户编号数组", example = "1")
private List<Long> userIds;
}
@Schema(description = "操作按钮设置")
@ -162,6 +223,28 @@ public class BpmSimpleModelNodeVO {
private Boolean enable;
}
@Schema(description = "条件设置")
@Data
@Valid
// 仅用于条件节点 BpmSimpleModelNodeType.CONDITION_NODE
public static class ConditionSetting {
@Schema(description = "条件类型", example = "1")
@InEnum(BpmSimpleModeConditionTypeEnum.class)
private Integer conditionType;
@Schema(description = "条件表达式", example = "${day>3}")
private String conditionExpression;
@Schema(description = "是否默认条件", example = "true")
private Boolean defaultFlow;
/**
*
*/
private ConditionGroups conditionGroups;
}
@Schema(description = "条件组")
@Data
@Valid
@ -208,5 +291,75 @@ public class BpmSimpleModelNodeVO {
private String rightSide;
}
// TODO @芋艿:条件;建议可以固化的一些选项;然后有个表达式兜底;要支持
@Schema(description = "延迟器")
@Data
@Valid
public static class DelaySetting {
@Schema(description = "延迟时间类型", example = "1")
@NotNull(message = "延迟时间类型不能为空")
@InEnum(BpmDelayTimerTypeEnum.class)
private Integer delayType;
@Schema(description = "延迟时间表达式", example = "PT1H,2025-01-01T00:00:00")
@NotEmpty(message = "延迟时间表达式不能为空")
private String delayTime;
}
@Schema(description = "路由分支")
@Data
@Valid
public static class RouterSetting {
@Schema(description = "节点 Id", example = "Activity_xxx") // 跳转到该节点
@NotEmpty(message = "节点 Id 不能为空")
private String nodeId;
@Schema(description = "条件类型", example = "1")
@InEnum(BpmSimpleModeConditionTypeEnum.class)
@NotNull(message = "条件类型不能为空")
private Integer conditionType;
@Schema(description = "条件表达式", example = "${day>3}")
private String conditionExpression;
@Schema(description = "条件组", example = "{}")
private ConditionGroups conditionGroups;
}
@Schema(description = "触发器节点配置")
@Data
@Valid
public static class TriggerSetting {
@Schema(description = "触发器类型", example = "1")
@InEnum(BpmTriggerTypeEnum.class)
@NotNull(message = "触发器类型不能为空")
private Integer type;
/**
* http
*/
@Valid
private HttpRequestTriggerSetting httpRequestSetting;
@Schema(description = "http 请求触发器设置", example = "{}")
@Data
public static class HttpRequestTriggerSetting {
@Schema(description = "请求路径", example = "http://127.0.0.1")
@NotEmpty(message = "请求 URL 不能为空")
@URL(message = "请求 URL 格式不正确")
private String url;
@Schema(description = "请求头参数设置", example = "[]")
@Valid
private List<HttpRequestParam> header;
@Schema(description = "请求头参数设置", example = "[]")
@Valid
private List<HttpRequestParam> body;
}
}
}

View File

@ -74,8 +74,10 @@ public class BpmProcessInstanceController {
convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId));
Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(
convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory));
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId));
return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult,
processDefinitionMap, categoryMap, taskMap, null, null));
processDefinitionMap, categoryMap, taskMap, null, null, processDefinitionInfoMap));
}
@GetMapping("/manager-page")
@ -101,8 +103,10 @@ public class BpmProcessInstanceController {
convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId())));
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId));
return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult,
processDefinitionMap, categoryMap, taskMap, userMap, deptMap));
processDefinitionMap, categoryMap, taskMap, userMap, deptMap, processDefinitionInfoMap));
}
@PostMapping("/create")

View File

@ -7,7 +7,9 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
@ -50,6 +52,8 @@ public class BpmTaskController {
private BpmProcessInstanceService processInstanceService;
@Resource
private BpmFormService formService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
private AdminUserApi adminUserApi;
@ -70,7 +74,9 @@ public class BpmTaskController {
convertSet(pageResult.getList(), Task::getProcessInstanceId));
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
return success(BpmTaskConvert.INSTANCE.buildTodoTaskPage(pageResult, processInstanceMap, userMap));
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
convertSet(pageResult.getList(), Task::getProcessDefinitionId));
return success(BpmTaskConvert.INSTANCE.buildTodoTaskPage(pageResult, processInstanceMap, userMap, processDefinitionInfoMap));
}
@GetMapping("done-page")
@ -87,7 +93,9 @@ public class BpmTaskController {
convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId));
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, null));
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId));
return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, null, processDefinitionInfoMap));
}
@GetMapping("manager-page")
@ -108,7 +116,9 @@ public class BpmTaskController {
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap));
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId));
return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap, processDefinitionInfoMap));
}
@GetMapping("/list-by-process-instance-id")

View File

@ -101,6 +101,9 @@ public class BpmApprovalDetailRespVO {
@Schema(description = "审批意见", example = "同意")
private String reason;
@Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
private String signPicUrl;
}
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
@ -19,6 +20,9 @@ public class BpmProcessInstanceRespVO {
@Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String name;
@Schema(description = "流程摘要")
private List<KeyValue<String, String>> summary; // 只有流程表单,才有摘要!
@Schema(description = "流程分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private String category;
@Schema(description = "流程分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "请假")

View File

@ -14,10 +14,12 @@ public class BpmTaskApproveReqVO {
@NotEmpty(message = "任务编号不能为空")
private String id;
@Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!")
@NotEmpty(message = "审批意见不能为空")
@Schema(description = "审批意见", example = "不错不错!")
private String reason;
@Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
private String signPicUrl;
@Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED)
private Map<String, Object> variables;

View File

@ -14,7 +14,6 @@ public class BpmTaskRejectReqVO {
private String id;
@Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!")
@NotEmpty(message = "审批意见不能为空")
private String reason;
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
@ -78,6 +79,16 @@ public class BpmTaskRespVO {
@Schema(description = "操作按钮设置值")
private Map<Integer, OperationButtonSetting> buttonsSetting;
@Schema(description = "是否需要签名", example = "false")
private Boolean signEnable;
@Schema(description = "是否填写审批意见", example = "false")
private Boolean reasonRequire;
// TODO @lesan要不放到 processInstance 里面?因为摘要是流程实例的,不是流程任务的
@Schema(description = "流程摘要", example = "[]")
private List<KeyValue<String, String>> summary; // 只有流程表单,才有摘要!
@Data
@Schema(description = "流程实例")
public static class ProcessInstance {

View File

@ -58,7 +58,8 @@ public interface BpmProcessInstanceConvert {
Map<String, BpmCategoryDO> categoryMap,
Map<String, List<Task>> taskMap,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
Map<Long, DeptRespDTO> deptMap,
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap) {
PageResult<BpmProcessInstanceRespVO> vpPageResult = BeanUtils.toBean(pageResult, BpmProcessInstanceRespVO.class);
for (int i = 0; i < pageResult.getList().size(); i++) {
BpmProcessInstanceRespVO respVO = vpPageResult.getList().get(i);
@ -76,6 +77,9 @@ public interface BpmProcessInstanceConvert {
MapUtils.findAndThen(deptMap, startUser.getDeptId(), dept -> respVO.getStartUser().setDeptName(dept.getName()));
}
}
// 摘要
respVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(respVO.getProcessDefinitionId()),
pageResult.getList().get(i).getProcessVariables()));
}
return vpPageResult;
}
@ -186,7 +190,8 @@ public interface BpmProcessInstanceConvert {
return null;
}
return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class)
.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task));
.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task))
.setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task));
}
default Set<Long> parseUserIds(HistoricProcessInstance processInstance,

View File

@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
@ -41,7 +42,8 @@ public interface BpmTaskConvert {
default PageResult<BpmTaskRespVO> buildTodoTaskPage(PageResult<Task> pageResult,
Map<String, ProcessInstance> processInstanceMap,
Map<Long, AdminUserRespDTO> userMap) {
Map<Long, AdminUserRespDTO> userMap,
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap) {
return BeanUtils.toBean(pageResult, BpmTaskRespVO.class, taskVO -> {
ProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId());
if (processInstance == null) {
@ -50,13 +52,17 @@ public interface BpmTaskConvert {
taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
// 摘要
taskVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),
processInstance.getProcessVariables()));
});
}
default PageResult<BpmTaskRespVO> buildTaskPage(PageResult<HistoricTaskInstance> pageResult,
Map<String, HistoricProcessInstance> processInstanceMap,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
Map<Long, DeptRespDTO> deptMap,
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap) {
List<BpmTaskRespVO> taskVOList = CollectionUtils.convertList(pageResult.getList(), task -> {
BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class);
taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task));
@ -72,6 +78,9 @@ public interface BpmTaskConvert {
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
// 摘要
taskVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),
processInstance.getProcessVariables()));
}
return taskVO;
});

View File

@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
@ -150,4 +152,34 @@ public class BpmProcessDefinitionInfoDO extends BaseDO {
@TableField(typeHandler = StringListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤
private List<Long> managerUserIds;
/**
*
*/
private Boolean allowCancelRunningProcess;
/**
* ID
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private BpmModelMetaInfoVO.ProcessIdRule processIdRule;
/**
*
*
* {@link BpmAutoApproveTypeEnum}
*/
private Integer autoApprovalType;
/**
*
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private BpmModelMetaInfoVO.TitleSetting titleSetting;
/**
*
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private BpmModelMetaInfoVO.SummarySetting summarySetting;
}

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessListenerTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -42,7 +43,7 @@ public class BpmProcessListenerDO extends BaseDO {
/**
*
*
* {@link cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessListenerType}
* {@link BpmProcessListenerTypeEnum}
*
* 1. executionExecutionListener <a href="https://tkjohn.github.io/flowable-userguide/#executionListeners"></a>
* 2. taskTaskListener <a href="https://tkjohn.github.io/flowable-userguide/#taskListeners"></a>

View File

@ -7,8 +7,6 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessI
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface BpmProcessInstanceCopyMapper extends BaseMapperX<BpmProcessInstanceCopyDO> {
@ -20,9 +18,8 @@ public interface BpmProcessInstanceCopyMapper extends BaseMapperX<BpmProcessInst
.orderByDesc(BpmProcessInstanceCopyDO::getId));
}
default List<BpmProcessInstanceCopyDO> selectListByProcessInstanceIdAndActivityId(String processInstanceId, String activityId) {
return selectList(BpmProcessInstanceCopyDO::getProcessInstanceId, processInstanceId,
BpmProcessInstanceCopyDO::getActivityId, activityId);
default void deleteByProcessInstanceId(String processInstanceId) {
delete(BpmProcessInstanceCopyDO::getProcessInstanceId, processInstanceId);
}
}

View File

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.bpm.dal.redis;
import cn.hutool.core.date.DateUtil;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;
import java.time.Duration;
import java.time.LocalDateTime;
/**
* BPM Id Redis DAO
*
* @author Lesan
*/
@Repository
public class BpmProcessIdRedisDAO {
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* 使 processIdRule
*
* @param processIdRule
* @return
*/
public String generate(BpmModelMetaInfoVO.ProcessIdRule processIdRule) {
// 生成日期前缀
String infix = "";
switch (processIdRule.getInfix()) {
case "DAY":
infix = DateUtil.format(LocalDateTime.now(), "yyyyMMDD");
break;
case "HOUR":
infix = DateUtil.format(LocalDateTime.now(), "yyyyMMDDHH");
break;
case "MINUTE":
infix = DateUtil.format(LocalDateTime.now(), "yyyyMMDDHHmm");
break;
case "SECOND":
infix = DateUtil.format(LocalDateTime.now(), "yyyyMMDDHHmmss");
break;
}
// 生成序号
String noPrefix = processIdRule.getPrefix() + infix + processIdRule.getPostfix();
String key = RedisKeyConstants.BPM_PROCESS_ID + noPrefix;
Long no = stringRedisTemplate.opsForValue().increment(key);
stringRedisTemplate.expire(key, Duration.ofDays(1L));
return noPrefix + String.format("%0" + processIdRule.getLength() + "d", no);
}
}

View File

@ -0,0 +1,15 @@
package cn.iocoder.yudao.module.bpm.dal.redis;
/**
* BPM Redis Key
*
* @author
*/
public interface RedisKeyConstants {
/**
* ID
*/
String BPM_PROCESS_ID = "bpm:process_id:";
}

View File

@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
@ -91,35 +92,39 @@ public class BpmTaskCandidateInvoker {
*/
@DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致找不到候选人
public Set<Long> calculateUsersByTask(DelegateExecution execution) {
// 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过
FlowElement flowElement = execution.getCurrentFlowElement();
Integer approveType = BpmnModelUtils.parseApproveType(flowElement);
if (ObjectUtils.equalsAny(approveType,
BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(),
BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) {
return new HashSet<>();
}
// 注意解决极端情况下Flowable 异步调用,导致租户 id 丢失的情况
// 例如说SIMPLE 延迟器在 trigger 的时候!!!
return FlowableUtils.execute(execution.getTenantId(), () -> {
// 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过
FlowElement flowElement = execution.getCurrentFlowElement();
Integer approveType = BpmnModelUtils.parseApproveType(flowElement);
if (ObjectUtils.equalsAny(approveType,
BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(),
BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) {
return new HashSet<>();
}
// 1.1 计算任务的候选人
Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement);
String param = BpmnModelUtils.parseCandidateParam(flowElement);
Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByTask(execution, param);
// 1.2 移除被禁用的用户
removeDisableUsers(userIds);
// 1.1 计算任务的候选人
Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement);
String param = BpmnModelUtils.parseCandidateParam(flowElement);
Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByTask(execution, param);
// 1.2 移除被禁用的用户
removeDisableUsers(userIds);
// 2. 候选人为空时,根据“审批人为空”的配置补充
if (CollUtil.isEmpty(userIds)) {
userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy())
.calculateUsersByTask(execution, param);
// ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!!
}
// 2. 候选人为空时,根据“审批人为空”的配置补充
if (CollUtil.isEmpty(userIds)) {
userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy())
.calculateUsersByTask(execution, param);
// ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!!
}
// 3. 移除发起人的用户
ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class)
.getProcessInstance(execution.getProcessInstanceId());
Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId());
removeStartUserIfSkip(userIds, flowElement, Long.valueOf(processInstance.getStartUserId()));
return userIds;
// 3. 移除发起人的用户
ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class)
.getProcessInstance(execution.getProcessInstanceId());
Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId());
removeStartUserIfSkip(userIds, flowElement, Long.valueOf(processInstance.getStartUserId()));
return userIds;
});
}
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId,

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -16,7 +16,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum BpmTaskCandidateStrategyEnum implements IntArrayValuable {
public enum BpmTaskCandidateStrategyEnum implements ArrayValuable<Integer> {
ROLE(10, "角色"),
DEPT_MEMBER(20, "部门的成员"), // 包括负责人
@ -35,7 +35,7 @@ public enum BpmTaskCandidateStrategyEnum implements IntArrayValuable {
ASSIGN_EMPTY(1, "审批人为空"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmTaskCandidateStrategyEnum::getStrategy).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmTaskCandidateStrategyEnum::getStrategy).toArray(Integer[]::new);
/**
*
@ -51,7 +51,7 @@ public enum BpmTaskCandidateStrategyEnum implements IntArrayValuable {
}
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -100,6 +100,15 @@ public interface BpmnModelConstants {
*/
String BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE = "enable";
/**
* BPMN ExtensionElement
*/
String TRIGGER_TYPE = "triggerType";
/**
* BPMN ExtensionElement
*/
String TRIGGER_PARAM = "triggerParam";
/**
* BPMN Start Event Node Id
*/
@ -110,4 +119,14 @@ public interface BpmnModelConstants {
*/
String START_USER_NODE_ID = "StartUserNode";
/**
*
*/
String SIGN_ENABLE = "signEnable";
/**
*
*/
String REASON_REQUIRE = "reasonRequire";
}

View File

@ -43,6 +43,24 @@ public class BpmnVariableConstants {
* @see ProcessInstance#getProcessVariables()
*/
public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = "RETURN_FLAG_%s";
/**
* -
*
* @see ProcessInstance#getProcessVariables()
* @see <a href="https://blog.csdn.net/weixin_42065235/article/details/126039993">Flowable/ActivitiSkipExpression </a>
*/
public static final String PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED";
/**
* -
*
* format
*/
public static final String PROCESS_START_TIME = "PROCESS_START_TIME";
/**
* -
*/
public static final String PROCESS_DEFINITION_NAME = "PROCESS_DEFINITION_NAME";
/**
* -
@ -58,5 +76,9 @@ public class BpmnVariableConstants {
* @see org.flowable.task.api.Task#getTaskLocalVariables()
*/
public static final String TASK_VARIABLE_REASON = "TASK_REASON";
/**
* - URL
*/
public static final String TASK_SIGN_PIC_URL = "TASK_SIGN_PIC_URL";
}

View File

@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableSet;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@ -22,6 +23,7 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent
public static final Set<FlowableEngineEventType> PROCESS_INSTANCE_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
.add(FlowableEngineEventType.PROCESS_COMPLETED)
.add(FlowableEngineEventType.PROCESS_CANCELLED)
.build();
@Resource
@ -37,4 +39,10 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent
processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity());
}
@Override // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法
protected void processCancelled(FlowableCancelledEvent event) {
ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId());
processInstanceService.processProcessInstanceCompleted(processInstance);
}
}

View File

@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventTypeEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
@ -97,16 +97,20 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
BoundaryEvent boundaryEvent = (BoundaryEvent) element;
String boundaryEventType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent,
BpmnModelConstants.BOUNDARY_EVENT_TYPE);
BpmBoundaryEventType bpmTimerBoundaryEventType = BpmBoundaryEventType.typeOf(NumberUtils.parseInt(boundaryEventType));
if (ObjectUtil.notEqual(bpmTimerBoundaryEventType, BpmBoundaryEventType.USER_TASK_TIMEOUT)) {
return;
}
BpmBoundaryEventTypeEnum bpmTimerBoundaryEventType = BpmBoundaryEventTypeEnum.typeOf(NumberUtils.parseInt(boundaryEventType));
// 2. 处理超时
String timeoutHandlerType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent,
BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_TYPE);
String taskKey = boundaryEvent.getAttachedToRefId();
taskService.processTaskTimeout(event.getProcessInstanceId(), taskKey, NumberUtils.parseInt(timeoutHandlerType));
if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT)) {
// 2.1 用户任务超时处理
String timeoutHandlerType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent,
BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_TYPE);
String taskKey = boundaryEvent.getAttachedToRefId();
taskService.processTaskTimeout(event.getProcessInstanceId(), taskKey, NumberUtils.parseInt(timeoutHandlerType));
// 2.2 延迟器超时处理
} else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT)) {
String taskKey = boundaryEvent.getAttachedToRefId();
taskService.processDelayTimerTimeout(event.getProcessInstanceId(), taskKey);
}
}
}

View File

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
import java.util.EnumMap;
import java.util.List;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate.BEAN_NAME;
/**
* {@link JavaDelegate}
* <p>
* Simple 使
*
* @author jason
*/
@Component(BEAN_NAME)
@Slf4j
public class BpmTriggerTaskDelegate implements JavaDelegate {
public static final String BEAN_NAME = "bpmTriggerTaskDelegate";
@Resource
private List<BpmTrigger> triggers;
private final EnumMap<BpmTriggerTypeEnum, BpmTrigger> triggerMap = new EnumMap<>(BpmTriggerTypeEnum.class);
@PostConstruct
private void init() {
triggers.forEach(trigger -> triggerMap.put(trigger.getType(), trigger));
}
@Override
public void execute(DelegateExecution execution) {
FlowElement flowElement = execution.getCurrentFlowElement();
BpmTriggerTypeEnum bpmTriggerType = BpmnModelUtils.parserTriggerType(flowElement);
BpmTrigger bpmTrigger = triggerMap.get(bpmTriggerType);
if (bpmTrigger == null) {
log.error("[execute][FlowElement({}), {} 找不到匹配的触发器]", execution.getCurrentActivityId(), flowElement);
return;
}
bpmTrigger.execute(execution.getProcessInstanceId(), BpmnModelUtils.parserTriggerParam(flowElement));
}
}

View File

@ -1,19 +1,19 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
import cn.iocoder.yudao.module.bpm.enums.definition.*;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
@ -22,6 +22,7 @@ import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.impl.util.io.BytesStreamSource;
import org.flowable.engine.impl.el.FixedValue;
import java.util.*;
@ -170,9 +171,9 @@ public class BpmnModelUtils {
* @param userTask
* @return
*/
public static BpmUserTaskRejectHandlerType parseRejectHandlerType(FlowElement userTask) {
public static BpmUserTaskRejectHandlerTypeEnum parseRejectHandlerType(FlowElement userTask) {
Integer rejectHandlerType = NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE));
return BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType);
return BpmUserTaskRejectHandlerTypeEnum.typeOf(rejectHandlerType);
}
/**
@ -346,6 +347,62 @@ public class BpmnModelUtils {
return Optional.ofNullable(extensionElement).map(ExtensionElement::getElementText).orElse(null);
}
public static void addSignEnable(Boolean signEnable, FlowElement userTask) {
addExtensionElement(userTask, SIGN_ENABLE,
ObjUtil.isNotNull(signEnable) ? signEnable.toString() : Boolean.FALSE.toString());
}
public static Boolean parseSignEnable(BpmnModel bpmnModel, String flowElementId) {
FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);
if (flowElement == null) {
return false;
}
List<ExtensionElement> extensionElements = flowElement.getExtensionElements().get(SIGN_ENABLE);
if (CollUtil.isEmpty(extensionElements)) {
return false;
}
return Convert.toBool(extensionElements.get(0).getElementText(), false);
}
public static void addReasonRequire(Boolean reasonRequire, FlowElement userTask) {
addExtensionElement(userTask, REASON_REQUIRE,
ObjUtil.isNotNull(reasonRequire) ? reasonRequire.toString() : Boolean.FALSE.toString());
}
public static Boolean parseReasonRequire(BpmnModel bpmnModel, String flowElementId) {
FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);
if (flowElement == null) {
return false;
}
List<ExtensionElement> extensionElements = flowElement.getExtensionElements().get(REASON_REQUIRE);
if (CollUtil.isEmpty(extensionElements)) {
return false;
}
return Convert.toBool(extensionElements.get(0).getElementText(), false);
}
public static void addListenerConfig(FlowableListener flowableListener, BpmSimpleModelNodeVO.ListenerHandler handler) {
FieldExtension fieldExtension = new FieldExtension();
fieldExtension.setFieldName("listenerConfig");
fieldExtension.setStringValue(JsonUtils.toJsonString(handler));
flowableListener.getFieldExtensions().add(fieldExtension);
}
public static BpmSimpleModelNodeVO.ListenerHandler parseListenerConfig(FixedValue fixedValue) {
String expressionText = fixedValue.getExpressionText();
Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText);
return JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ListenerHandler.class);
}
public static BpmTriggerTypeEnum parserTriggerType(FlowElement flowElement) {
Integer triggerType = NumberUtils.parseInt(parseExtensionElement(flowElement, TRIGGER_TYPE));
return BpmTriggerTypeEnum.typeOf(triggerType);
}
public static String parserTriggerParam(FlowElement flowElement) {
return parseExtensionElement(flowElement, TRIGGER_PARAM);
}
// ========== BPM 简单查找相关的方法 ==========
/**
@ -777,7 +834,7 @@ public class BpmnModelUtils {
Object result = FlowableUtils.getExpressionValue(variables, express);
return Boolean.TRUE.equals(result);
} catch (FlowableException ex) {
log.error("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错", express, variables, ex);
log.error("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错]", express, variables, ex);
return Boolean.FALSE;
}
}

View File

@ -2,9 +2,15 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormFieldVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
import lombok.SneakyThrows;
import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.common.engine.api.variable.VariableContainer;
import org.flowable.common.engine.impl.el.ExpressionManager;
@ -18,10 +24,7 @@ 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;
import java.util.Objects;
import java.util.*;
import java.util.concurrent.Callable;
/**
@ -67,6 +70,17 @@ public class FlowableUtils {
}
}
@SneakyThrows
public static <V> V execute(String tenantIdStr, Callable<V> callable) {
if (ObjectUtil.isEmpty(tenantIdStr)
|| Objects.equals(tenantIdStr, ProcessEngineConfiguration.NO_TENANT_ID)) {
return callable.call();
} else {
Long tenantId = Long.valueOf(tenantIdStr);
return TenantUtils.execute(tenantId, callable);
}
}
// ========== Execution 相关的工具方法 ==========
/**
@ -179,6 +193,68 @@ public class FlowableUtils {
BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
}
// TODO @lesan如果值是 null 的情况,可能要调研下飞书、钉钉,是不是不返回哈!
/**
*
*
* {@link BpmModelFormTypeEnum#getType()}
*
*
* @param processDefinitionInfo
* @param processVariables variables
* @return
*/
public static List<KeyValue<String, String>> getSummary(BpmProcessDefinitionInfoDO processDefinitionInfo,
Map<String, Object> processVariables) {
// TODO @lesan建议 if return减少 { 层级
if (ObjectUtil.isNotNull(processDefinitionInfo)
&& BpmModelFormTypeEnum.NORMAL.getType().equals(processDefinitionInfo.getFormType())) {
List<KeyValue<String, String>> summaryList = new ArrayList<>();
// TODO @lesan可以使用 CollUtils.convertMap 简化工作量哈。
Map<String, BpmFormFieldVO> formFieldsMap = new HashMap<>();
processDefinitionInfo.getFormFields().forEach(formFieldStr -> {
BpmFormFieldVO formField = JsonUtils.parseObject(formFieldStr, BpmFormFieldVO.class);
if (formField != null) {
formFieldsMap.put(formField.getField(), formField);
}
});
// TODO @lesan这里也可以 if return还是为了减少括号哈。这样就可以写注释情况一情况二
if (ObjectUtil.isNotNull(processDefinitionInfo.getSummarySetting())
&& Boolean.TRUE.equals(processDefinitionInfo.getSummarySetting().getEnable())) {
// TODO @lesan这里也可以通过 CollUtils.convertList 简化哈。
for (String item : processDefinitionInfo.getSummarySetting().getSummary()) {
BpmFormFieldVO formField = formFieldsMap.get(item);
if (formField != null) {
summaryList.add(new KeyValue<>(formField.getTitle(),
processVariables.getOrDefault(item, "").toString()));
}
}
} else {
// 默认展示前三个
/* TODO @lesanstream
* summaryList.addAll(formFieldsMap.entrySet().stream()
* .limit(3)
* .map(entry -> new KeyValue<>(entry.getValue().getTitle(),
* processVariables.getOrDefault(entry.getValue().getField(), "").toString()))
* .collect(Collectors.toList()));
*/
int j = 0;
for (Map.Entry<String, BpmFormFieldVO> entry : formFieldsMap.entrySet()) {
BpmFormFieldVO formField = entry.getValue();
if (j > 2) {
break;
}
summaryList.add(new KeyValue<>(formField.getTitle(),
processVariables.getOrDefault(formField.getField(), "").toString()));
j++;
}
}
return summaryList;
}
return null;
}
// ========== Task 相关的工具方法 ==========
/**
@ -201,6 +277,16 @@ public class FlowableUtils {
return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_REASON);
}
/**
* URL
*
* @param task
* @return URL
*/
public static String getTaskSignPicUrl(TaskInfo task) {
return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_SIGN_PIC_URL);
}
/**
*
*

View File

@ -5,21 +5,26 @@ import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups;
import cn.iocoder.yudao.module.bpm.enums.definition.*;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.engine.delegate.TaskListener;
import org.springframework.util.MultiValueMap;
import java.util.*;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*;
import static cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener.DELEGATE_EXPRESSION;
import static java.util.Arrays.asList;
/**
@ -32,12 +37,13 @@ import static java.util.Arrays.asList;
*/
public class SimpleModelUtils {
private static final Map<BpmSimpleModelNodeType, NodeConvert> NODE_CONVERTS = MapUtil.newHashMap();
private static final Map<BpmSimpleModelNodeTypeEnum, NodeConvert> NODE_CONVERTS = MapUtil.newHashMap();
static {
List<NodeConvert> converts = asList(new StartNodeConvert(), new EndNodeConvert(),
new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(),
new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert());
new DelayTimerNodeConvert(), new TriggerNodeConvert(),
new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert());
converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert));
}
@ -83,14 +89,14 @@ public class SimpleModelUtils {
private static BpmSimpleModelNodeVO buildStartNode() {
return new BpmSimpleModelNodeVO().setId(START_EVENT_NODE_ID)
.setName(BpmSimpleModelNodeType.START_NODE.getName())
.setType(BpmSimpleModelNodeType.START_NODE.getType());
.setName(BpmSimpleModelNodeTypeEnum.START_NODE.getName())
.setType(BpmSimpleModelNodeTypeEnum.START_NODE.getType());
}
/**
* FlowNode
*
* @param node SIMPLE
* @param node SIMPLE
* @param process BPMN
*/
private static void traverseNodeToBuildFlowNode(BpmSimpleModelNodeVO node, Process process) {
@ -98,7 +104,7 @@ public class SimpleModelUtils {
if (!isValidNode(node)) {
return;
}
BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());
BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType());
Assert.notNull(nodeType, "模型节点类型({})不支持", node.getType());
// 2. 处理当前节点
@ -108,7 +114,7 @@ public class SimpleModelUtils {
flowElements.forEach(process::addFlowElement);
// 3.1 情况一:如果当前是分支节点,并且存在条件节点,则处理每个条件的子节点
if (BpmSimpleModelNodeType.isBranchNode(node.getType())
if (BpmSimpleModelNodeTypeEnum.isBranchNode(node.getType())
&& CollUtil.isNotEmpty(node.getConditionNodes())) {
// 注意:这里的 item.getChildNode() 处理的是每个条件的子节点,不是处理条件
node.getConditionNodes().forEach(item -> traverseNodeToBuildFlowNode(item.getChildNode(), process));
@ -121,8 +127,8 @@ public class SimpleModelUtils {
/**
* SequenceFlow
*
* @param process Bpmn
* @param node
* @param process Bpmn
* @param node
* @param targetNodeId ID
*/
private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) {
@ -131,14 +137,14 @@ public class SimpleModelUtils {
return;
}
// 1.2 END_NODE 直接返回
BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());
BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType());
Assert.notNull(nodeType, "模型节点类型不支持");
if (nodeType == BpmSimpleModelNodeType.END_NODE) {
if (nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) {
return;
}
// 2.1 情况一:普通节点
if (!BpmSimpleModelNodeType.isBranchNode(node.getType())) {
if (!BpmSimpleModelNodeTypeEnum.isBranchNode(node.getType())) {
traverseNormalNodeToBuildSequenceFlow(process, node, targetNodeId);
} else {
// 2.2 情况二:分支节点
@ -149,8 +155,8 @@ public class SimpleModelUtils {
/**
* SequenceFlow
*
* @param process Bpmn
* @param node
* @param process Bpmn
* @param node
* @param targetNodeId ID
*/
private static void traverseNormalNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) {
@ -158,7 +164,7 @@ public class SimpleModelUtils {
boolean isChildNodeValid = isValidNode(childNode);
// 情况一:有“子”节点,则建立连线
// 情况二:没有“子节点”,则直接跟 targetNodeId 建立连线。例如说,结束节点、条件分支(分支节点的孩子节点或聚合节点)的最后一个节点
String finalTargetNodeId = isChildNodeValid? childNode.getId() : targetNodeId;
String finalTargetNodeId = isChildNodeValid ? childNode.getId() : targetNodeId;
SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId);
process.addFlowElement(sequenceFlow);
@ -171,53 +177,68 @@ public class SimpleModelUtils {
/**
* SequenceFlow
*
* @param process Bpmn
* @param node
* @param process Bpmn
* @param node
* @param targetNodeId ID
*/
private static void traverseBranchNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) {
BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());
BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType());
BpmSimpleModelNodeVO childNode = node.getChildNode();
List<BpmSimpleModelNodeVO> conditionNodes = node.getConditionNodes();
Assert.notEmpty(conditionNodes, "分支节点的条件节点不能为空");
// TODO @芋艿 路由分支没有conditionNodes 这里注释会影响吗?@jason一起帮忙瞅瞅
// Assert.notEmpty(conditionNodes, "分支节点的条件节点不能为空");
// 分支终点节点 ID
String branchEndNodeId = null;
if (nodeType == BpmSimpleModelNodeType.CONDITION_BRANCH_NODE) { // 条件分支
if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE
|| nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) { // 条件分支或路由分支
// 分两种情况 1. 分支节点有孩子节点为孩子节点 Id 2. 分支节点孩子为无效节点时 (分支嵌套且为分支最后一个节点) 为分支终点节点 ID
branchEndNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId;
} else if (nodeType == BpmSimpleModelNodeType.PARALLEL_BRANCH_NODE
|| nodeType == BpmSimpleModelNodeType.INCLUSIVE_BRANCH_NODE) { // 并行分支或包容分支
} else if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE
|| nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { // 并行分支或包容分支
// 分支节点:分支终点节点 Id 为程序创建的网关集合节点。目前不会从前端传入。
branchEndNodeId = buildGatewayJoinId(node.getId());
}
Assert.notEmpty(branchEndNodeId, "分支终点节点 Id 不能为空");
// 3. 遍历分支节点
// 下面的注释,以如下情况举例子。分支 1A->B->C->D->E分支 2A->D->E。其中A 为分支节点, D 为 A 孩子节点
for (BpmSimpleModelNodeVO item : conditionNodes) {
Assert.isTrue(Objects.equals(item.getType(), BpmSimpleModelNodeType.CONDITION_NODE.getType()),
"条件节点类型({})不符合", item.getType());
BpmSimpleModelNodeVO conditionChildNode = item.getChildNode();
// 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况
if (isValidNode(conditionChildNode)) {
// 3.1.1 建立与后续的节点的连线。例如说,建立 A->B 的连线
SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), item);
process.addFlowElement(sequenceFlow);
// 3.1.2 递归调用后续节点连线。例如说,建立 B->C->D 的连线
traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId);
} else {
// 3.2 分支没有后续节点。例如说,建立 A->D 的连线
SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, item);
if (nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) {
// 路由分支遍历
for (BpmSimpleModelNodeVO.RouterSetting router : node.getRouterGroups()) {
SequenceFlow sequenceFlow = RouteBranchNodeConvert.buildSequenceFlow(node.getId(), router);
process.addFlowElement(sequenceFlow);
}
} else {
// 下面的注释,以如下情况举例子。分支 1A->B->C->D->E分支 2A->D->E。其中A 为分支节点, D 为 A 孩子节点
for (BpmSimpleModelNodeVO item : conditionNodes) {
Assert.isTrue(Objects.equals(item.getType(), BpmSimpleModelNodeTypeEnum.CONDITION_NODE.getType()),
"条件节点类型({})不符合", item.getType());
BpmSimpleModelNodeVO conditionChildNode = item.getChildNode();
// 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况
if (isValidNode(conditionChildNode)) {
// 3.1.1 建立与后续的节点的连线。例如说,建立 A->B 的连线
SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), item);
process.addFlowElement(sequenceFlow);
// 3.1.2 递归调用后续节点连线。例如说,建立 B->C->D 的连线
traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId);
} else {
// 3.2 分支没有后续节点。例如说,建立 A->D 的连线
SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, item);
process.addFlowElement(sequenceFlow);
}
}
}
// 4. 如果是并行分支、包容分支,由于是程序创建的聚合网关,需要手工创建聚合网关和下一个节点的连线
if (nodeType == BpmSimpleModelNodeType.PARALLEL_BRANCH_NODE
|| nodeType == BpmSimpleModelNodeType.INCLUSIVE_BRANCH_NODE ) {
// 4.1 如果是并行分支、包容分支,由于是程序创建的聚合网关,需要手工创建聚合网关和下一个节点的连线
if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE
|| nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) {
String nextNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId;
SequenceFlow sequenceFlow = buildBpmnSequenceFlow(branchEndNodeId, nextNodeId);
process.addFlowElement(sequenceFlow);
// 4.2 如果是路由分支,需要连接后续节点为默认路由
} else if (nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) {
SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), branchEndNodeId, node.getRouterDefaultFlowId(),
null, null);
process.addFlowElement(sequenceFlow);
}
// 5. 递归调用后续节点 继续递归。例如说,建立 D->E 的连线
@ -253,7 +274,7 @@ public class SimpleModelUtils {
}
public static boolean isSequentialApproveNode(BpmSimpleModelNodeVO node) {
return BpmSimpleModelNodeType.APPROVE_NODE.getType().equals(node.getType())
return BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType().equals(node.getType())
&& BpmUserTaskApproveMethodEnum.SEQUENTIAL.getMethod().equals(node.getApproveMethod());
}
@ -269,7 +290,7 @@ public class SimpleModelUtils {
throw new UnsupportedOperationException("请实现该方法");
}
BpmSimpleModelNodeType getType();
BpmSimpleModelNodeTypeEnum getType();
}
@ -284,8 +305,8 @@ public class SimpleModelUtils {
}
@Override
public BpmSimpleModelNodeType getType() {
return BpmSimpleModelNodeType.START_NODE;
public BpmSimpleModelNodeTypeEnum getType() {
return BpmSimpleModelNodeTypeEnum.START_NODE;
}
}
@ -302,8 +323,8 @@ public class SimpleModelUtils {
}
@Override
public BpmSimpleModelNodeType getType() {
return BpmSimpleModelNodeType.END_NODE;
public BpmSimpleModelNodeTypeEnum getType() {
return BpmSimpleModelNodeTypeEnum.END_NODE;
}
}
@ -331,8 +352,8 @@ public class SimpleModelUtils {
}
@Override
public BpmSimpleModelNodeType getType() {
return BpmSimpleModelNodeType.START_USER_NODE;
public BpmSimpleModelNodeTypeEnum getType() {
return BpmSimpleModelNodeTypeEnum.START_USER_NODE;
}
}
@ -355,8 +376,8 @@ public class SimpleModelUtils {
}
@Override
public BpmSimpleModelNodeType getType() {
return BpmSimpleModelNodeType.APPROVE_NODE;
public BpmSimpleModelNodeTypeEnum getType() {
return BpmSimpleModelNodeTypeEnum.APPROVE_NODE;
}
/**
@ -384,7 +405,7 @@ public class SimpleModelUtils {
boundaryEvent.addEventDefinition(eventDefinition);
// 2.1 添加定时器边界事件类型
addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventType.USER_TASK_TIMEOUT.getType());
addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT.getType());
// 2.2 添加超时执行动作元素
addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_TYPE, timeoutHandler.getType());
return boundaryEvent;
@ -419,9 +440,49 @@ public class SimpleModelUtils {
if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) {
userTask.setDueDate(node.getTimeoutHandler().getTimeDuration());
}
// 设置监听器
addUserTaskListener(node, userTask);
// 添加是否需要签名
addSignEnable(node.getSignEnable(), userTask);
// 审批意见
addReasonRequire(node.getReasonRequire(), userTask);
return userTask;
}
private void addUserTaskListener(BpmSimpleModelNodeVO node, UserTask userTask) {
List<FlowableListener> flowableListeners = new ArrayList<>(3);
if (node.getTaskCreateListener() != null
&& Boolean.TRUE.equals(node.getTaskCreateListener().getEnable())) {
FlowableListener flowableListener = new FlowableListener();
flowableListener.setEvent(TaskListener.EVENTNAME_CREATE);
flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
flowableListener.setImplementation(DELEGATE_EXPRESSION);
addListenerConfig(flowableListener, node.getTaskCreateListener());
flowableListeners.add(flowableListener);
}
if (node.getTaskAssignListener() != null
&& Boolean.TRUE.equals(node.getTaskAssignListener().getEnable())) {
FlowableListener flowableListener = new FlowableListener();
flowableListener.setEvent(TaskListener.EVENTNAME_ASSIGNMENT);
flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
flowableListener.setImplementation(DELEGATE_EXPRESSION);
addListenerConfig(flowableListener, node.getTaskAssignListener());
flowableListeners.add(flowableListener);
}
if (node.getTaskCompleteListener() != null
&& Boolean.TRUE.equals(node.getTaskCompleteListener().getEnable())) {
FlowableListener flowableListener = new FlowableListener();
flowableListener.setEvent(TaskListener.EVENTNAME_COMPLETE);
flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
flowableListener.setImplementation(DELEGATE_EXPRESSION);
addListenerConfig(flowableListener, node.getTaskCompleteListener());
flowableListeners.add(flowableListener);
}
if (CollUtil.isNotEmpty(flowableListeners)) {
userTask.setTaskListeners(flowableListeners);
}
}
private void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) {
BpmUserTaskApproveMethodEnum approveMethodEnum = BpmUserTaskApproveMethodEnum.valueOf(approveMethod);
Assert.notNull(approveMethodEnum, "审批方式({})不能为空", approveMethodEnum);
@ -472,8 +533,8 @@ public class SimpleModelUtils {
}
@Override
public BpmSimpleModelNodeType getType() {
return BpmSimpleModelNodeType.COPY_NODE;
public BpmSimpleModelNodeTypeEnum getType() {
return BpmSimpleModelNodeTypeEnum.COPY_NODE;
}
}
@ -488,15 +549,15 @@ public class SimpleModelUtils {
// 设置默认的序列流(条件)
BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(node.getConditionNodes(),
item -> BooleanUtil.isTrue(item.getDefaultFlow()));
item -> BooleanUtil.isTrue(item.getConditionSetting().getDefaultFlow()));
Assert.notNull(defaultSeqFlow, "条件分支节点({})的默认序列流不能为空", node.getId());
exclusiveGateway.setDefaultFlow(defaultSeqFlow.getId());
return exclusiveGateway;
}
@Override
public BpmSimpleModelNodeType getType() {
return BpmSimpleModelNodeType.CONDITION_BRANCH_NODE;
public BpmSimpleModelNodeTypeEnum getType() {
return BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE;
}
}
@ -517,8 +578,8 @@ public class SimpleModelUtils {
}
@Override
public BpmSimpleModelNodeType getType() {
return BpmSimpleModelNodeType.PARALLEL_BRANCH_NODE;
public BpmSimpleModelNodeTypeEnum getType() {
return BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE;
}
}
@ -531,7 +592,7 @@ public class SimpleModelUtils {
inclusiveGateway.setId(node.getId());
// 设置默认的序列流(条件)
BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(node.getConditionNodes(),
item -> BooleanUtil.isTrue(item.getDefaultFlow()));
item -> BooleanUtil.isTrue(item.getConditionSetting().getDefaultFlow()));
Assert.notNull(defaultSeqFlow, "包容分支节点({})的默认序列流不能为空", node.getId());
inclusiveGateway.setDefaultFlow(defaultSeqFlow.getId());
// TODO @jasonsetName
@ -544,8 +605,8 @@ public class SimpleModelUtils {
}
@Override
public BpmSimpleModelNodeType getType() {
return BpmSimpleModelNodeType.INCLUSIVE_BRANCH_NODE;
public BpmSimpleModelNodeTypeEnum getType() {
return BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE;
}
}
@ -559,8 +620,8 @@ public class SimpleModelUtils {
}
@Override
public BpmSimpleModelNodeType getType() {
return BpmSimpleModelNodeType.CONDITION_NODE;
public BpmSimpleModelNodeTypeEnum getType() {
return BpmSimpleModelNodeTypeEnum.CONDITION_NODE;
}
public static SequenceFlow buildSequenceFlow(String sourceId, String targetId,
@ -575,12 +636,22 @@ public class SimpleModelUtils {
* @param node
*/
public static String buildConditionExpression(BpmSimpleModelNodeVO node) {
BpmSimpleModeConditionType conditionTypeEnum = BpmSimpleModeConditionType.valueOf(node.getConditionType());
if (conditionTypeEnum == BpmSimpleModeConditionType.EXPRESSION) {
return node.getConditionExpression();
return buildConditionExpression(node.getConditionSetting().getConditionType(), node.getConditionSetting().getConditionExpression(),
node.getConditionSetting().getConditionGroups());
}
public static String buildConditionExpression(BpmSimpleModelNodeVO.RouterSetting router) {
return buildConditionExpression(router.getConditionType(), router.getConditionExpression(),
router.getConditionGroups());
}
public static String buildConditionExpression(Integer conditionType, String conditionExpression,
ConditionGroups conditionGroups) {
BpmSimpleModeConditionTypeEnum conditionTypeEnum = BpmSimpleModeConditionTypeEnum.valueOf(conditionType);
if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.EXPRESSION) {
return conditionExpression;
}
if (conditionTypeEnum == BpmSimpleModeConditionType.RULE) {
ConditionGroups conditionGroups = node.getConditionGroups();
if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.RULE) {
if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) {
return null;
}
@ -605,6 +676,96 @@ public class SimpleModelUtils {
}
public static class DelayTimerNodeConvert implements NodeConvert {
@Override
public List<FlowElement> convertList(BpmSimpleModelNodeVO node) {
List<FlowElement> flowElements = new ArrayList<>(2);
// 1. 构建接收任务,通过接收任务可卡住节点
ReceiveTask receiveTask = new ReceiveTask();
receiveTask.setId(node.getId());
receiveTask.setName(node.getName());
flowElements.add(receiveTask);
// 2. 添加接收任务的 Timer Boundary Event
if (node.getDelaySetting() != null) {
// 2.1 定时器边界事件
BoundaryEvent boundaryEvent = new BoundaryEvent();
boundaryEvent.setId("Event-" + IdUtil.fastUUID());
boundaryEvent.setCancelActivity(false);
boundaryEvent.setAttachedToRef(receiveTask);
// 2.2 定义超时时间
TimerEventDefinition eventDefinition = new TimerEventDefinition();
if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) {
eventDefinition.setTimeDuration(node.getDelaySetting().getDelayTime());
} else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) {
eventDefinition.setTimeDate(node.getDelaySetting().getDelayTime());
}
boundaryEvent.addEventDefinition(eventDefinition);
addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType());
flowElements.add(boundaryEvent);
}
return flowElements;
}
@Override
public BpmSimpleModelNodeTypeEnum getType() {
return BpmSimpleModelNodeTypeEnum.DELAY_TIMER_NODE;
}
}
public static class TriggerNodeConvert implements NodeConvert {
@Override
public ServiceTask convert(BpmSimpleModelNodeVO node) {
// 触发器使用 ServiceTask 来实现
ServiceTask serviceTask = new ServiceTask();
serviceTask.setId(node.getId());
serviceTask.setName(node.getName());
serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
serviceTask.setImplementation("${" + BpmTriggerTaskDelegate.BEAN_NAME + "}");
if (node.getTriggerSetting() != null) {
addExtensionElement(serviceTask, TRIGGER_TYPE, node.getTriggerSetting().getType());
if (node.getTriggerSetting().getHttpRequestSetting() != null) {
// TODO @jason加个 addExtensionElementJson 方法,方便设置 JSON 类型的属性
addExtensionElement(serviceTask, TRIGGER_PARAM,
JsonUtils.toJsonString(node.getTriggerSetting().getHttpRequestSetting()));
}
}
return serviceTask;
}
@Override
public BpmSimpleModelNodeTypeEnum getType() {
return BpmSimpleModelNodeTypeEnum.TRIGGER_NODE;
}
}
public static class RouteBranchNodeConvert implements NodeConvert {
@Override
public ExclusiveGateway convert(BpmSimpleModelNodeVO node) {
ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
exclusiveGateway.setId(node.getId());
// 设置默认的序列流(条件)
node.setRouterDefaultFlowId("Flow_" + IdUtil.fastUUID());
exclusiveGateway.setDefaultFlow(node.getRouterDefaultFlowId());
return exclusiveGateway;
}
@Override
public BpmSimpleModelNodeTypeEnum getType() {
return BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE;
}
public static SequenceFlow buildSequenceFlow(String nodeId, BpmSimpleModelNodeVO.RouterSetting router) {
String conditionExpression = ConditionNodeConvert.buildConditionExpression(router);
return buildBpmnSequenceFlow(nodeId, router.getNodeId(), null, null, conditionExpression);
}
}
private static String buildGatewayJoinId(String id) {
return id + "_join";
}
@ -620,33 +781,33 @@ public class SimpleModelUtils {
}
private static void simulateNextNode(BpmSimpleModelNodeVO currentNode, Map<String, Object> variables,
List<BpmSimpleModelNodeVO> resultNodes) {
List<BpmSimpleModelNodeVO> resultNodes) {
// 如果不合法(包括为空),则直接结束
if (!isValidNode(currentNode)) {
return;
}
BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(currentNode.getType());
BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(currentNode.getType());
Assert.notNull(nodeType, "模型节点类型不支持");
// 情况START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE
if (nodeType == BpmSimpleModelNodeType.START_NODE
|| nodeType == BpmSimpleModelNodeType.START_USER_NODE
|| nodeType == BpmSimpleModelNodeType.APPROVE_NODE
|| nodeType == BpmSimpleModelNodeType.COPY_NODE
|| nodeType == BpmSimpleModelNodeType.END_NODE) {
if (nodeType == BpmSimpleModelNodeTypeEnum.START_NODE
|| nodeType == BpmSimpleModelNodeTypeEnum.START_USER_NODE
|| nodeType == BpmSimpleModelNodeTypeEnum.APPROVE_NODE
|| nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE
|| nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) {
// 添加元素
resultNodes.add(currentNode);
}
// 情况CONDITION_BRANCH_NODE 排它,只有一个满足条件的。如果没有,就走默认的
if (nodeType == BpmSimpleModelNodeType.CONDITION_BRANCH_NODE) {
if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE) {
// 查找满足条件的 BpmSimpleModelNodeVO 节点
BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(),
conditionNode -> !BooleanUtil.isTrue(conditionNode.getDefaultFlow())
&& evalConditionExpress(variables, conditionNode));
conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())
&& evalConditionExpress(variables, conditionNode));
if (matchConditionNode == null) {
matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(),
conditionNode -> BooleanUtil.isTrue(conditionNode.getDefaultFlow()));
conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()));
}
Assert.notNull(matchConditionNode, "找不到条件节点({})", currentNode);
// 遍历满足条件的 BpmSimpleModelNodeVO 节点
@ -654,14 +815,14 @@ public class SimpleModelUtils {
}
// 情况INCLUSIVE_BRANCH_NODE 包容,多个满足条件的。如果没有,就走默认的
if (nodeType == BpmSimpleModelNodeType.INCLUSIVE_BRANCH_NODE) {
if (nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) {
// 查找满足条件的 BpmSimpleModelNodeVO 节点
Collection<BpmSimpleModelNodeVO> matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(),
conditionNode -> !BooleanUtil.isTrue(conditionNode.getDefaultFlow())
conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())
&& evalConditionExpress(variables, conditionNode));
if (CollUtil.isEmpty(matchConditionNodes)) {
matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(),
conditionNode -> BooleanUtil.isTrue(conditionNode.getDefaultFlow()));
conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()));
}
Assert.isTrue(!matchConditionNodes.isEmpty(), "找不到条件节点({})", currentNode);
// 遍历满足条件的 BpmSimpleModelNodeVO 节点
@ -670,7 +831,7 @@ public class SimpleModelUtils {
}
// 情况PARALLEL_BRANCH_NODE 并行,都满足,都走
if (nodeType == BpmSimpleModelNodeType.PARALLEL_BRANCH_NODE) {
if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE) {
// 遍历所有 BpmSimpleModelNodeVO 节点
currentNode.getConditionNodes().forEach(matchConditionNode ->
simulateNextNode(matchConditionNode.getChildNode(), variables, resultNodes));
@ -684,4 +845,26 @@ public class SimpleModelUtils {
return BpmnModelUtils.evalConditionExpress(variables, ConditionNodeConvert.buildConditionExpression(conditionNode));
}
// TODO @芋艿:【高】要不要优化下,抽个 HttpUtils
/**
* HTTP
*
* @param params HTTP
* @param paramSettings HTTP
* @param processVariables
*/
public static void addHttpRequestParam(MultiValueMap<String, String> params,
List<BpmSimpleModelNodeVO.HttpRequestParam> paramSettings,
Map<String, Object> processVariables) {
if (CollUtil.isEmpty(paramSettings)) {
return;
}
paramSettings.forEach(item -> {
if (item.getType().equals(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())) {
params.add(item.getKey(), item.getValue());
} else if (item.getType().equals(BpmHttpRequestParamTypeEnum.FROM_FORM.getType())) {
params.add(item.getKey(), processVariables.get(item.getValue()).toString());
}
});
}
}

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.bpm.framework.security.config;
import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;
import cn.iocoder.yudao.module.bpm.enums.ApiConstants;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@ -30,6 +31,8 @@ public class SecurityConfiguration {
// Spring Boot Actuator 的安全配置
registry.requestMatchers("/actuator").permitAll()
.requestMatchers("/actuator/**").permitAll();
// RPC 服务的安全配置
registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll();
}
};

View File

@ -88,6 +88,14 @@ public interface BpmModelService {
*/
void deleteModel(Long userId, String id);
/**
*
*
* @param userId
* @param id
*/
void cleanModel(Long userId, String id);
/**
*
*

View File

@ -14,25 +14,33 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
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.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ModelQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
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.List;
import java.util.Map;
import java.util.Objects;
@ -63,6 +71,15 @@ public class BpmModelServiceImpl implements BpmModelService {
@Resource
private BpmTaskCandidateInvoker taskCandidateInvoker;
@Resource
private HistoryService historyService;
@Resource
private RuntimeService runtimeService;
@Resource
private TaskService taskService;
@Resource
private BpmProcessInstanceCopyService processInstanceCopyService;
@Override
public List<Model> getModelList(String name) {
ModelQuery modelQuery = repositoryService.createModelQuery();
@ -246,6 +263,35 @@ public class BpmModelServiceImpl implements BpmModelService {
updateProcessDefinitionSuspended(model.getDeploymentId());
}
@Override
public void cleanModel(Long userId, String id) {
// 1. 校验流程模型存在
Model model = validateModelManager(id, userId);
// 2. 清理所有流程数据
// 2.1 先取消所有正在运行的流程
List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()
.processDefinitionKey(model.getKey()).list();
processInstances.forEach(processInstance -> {
runtimeService.deleteProcessInstance(processInstance.getId(),
BpmReasonEnum.CANCEL_BY_SYSTEM.getReason());
historyService.deleteHistoricProcessInstance(processInstance.getId());
processInstanceCopyService.deleteProcessInstanceCopy(processInstance.getId());
});
// 2.2 再从历史中删除所有相关的流程数据
List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
.processDefinitionKey(model.getKey()).list();
historicProcessInstances.forEach(historicProcessInstance -> {
historyService.deleteHistoricProcessInstance(historicProcessInstance.getId());
processInstanceCopyService.deleteProcessInstanceCopy(historicProcessInstance.getId());
});
// 2.3 清理所有 Task
List<Task> tasks = taskService.createTaskQuery()
.processDefinitionKey(model.getKey()).list();
// TODO @lesan貌似传递一个 reason 会好点!
tasks.forEach(task -> taskService.deleteTask(task.getId()));
}
@Override
public void updateModelState(Long userId, String id, Integer state) {
// 1.1 校验流程模型存在

View File

@ -7,15 +7,14 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmPr
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessListenerDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessListenerMapper;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessListenerType;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessListenerValueType;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessListenerTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessListenerValueTypeEnum;
import jakarta.annotation.Resource;
import org.flowable.engine.delegate.JavaDelegate;
import org.flowable.engine.delegate.TaskListener;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
@ -53,15 +52,15 @@ public class BpmProcessListenerServiceImpl implements BpmProcessListenerService
private void validateCreateProcessListenerValue(BpmProcessListenerSaveReqVO createReqVO) {
// class 类型
if (createReqVO.getValueType().equals(BpmProcessListenerValueType.CLASS.getType())) {
if (createReqVO.getValueType().equals(BpmProcessListenerValueTypeEnum.CLASS.getType())) {
try {
Class<?> clazz = Class.forName(createReqVO.getValue());
if (createReqVO.getType().equals(BpmProcessListenerType.EXECUTION.getType())
&& !JavaDelegate.class.isAssignableFrom(clazz)) {
if (createReqVO.getType().equals(BpmProcessListenerTypeEnum.EXECUTION.getType())
&& !JavaDelegate.class.isAssignableFrom(clazz)) {
throw exception(PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR, createReqVO.getValue(),
JavaDelegate.class.getName());
} else if (createReqVO.getType().equals(BpmProcessListenerType.TASK.getType())
&& !TaskListener.class.isAssignableFrom(clazz)) {
} else if (createReqVO.getType().equals(BpmProcessListenerTypeEnum.TASK.getType())
&& !TaskListener.class.isAssignableFrom(clazz)) {
throw exception(PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR, createReqVO.getValue(),
TaskListener.class.getName());
}

View File

@ -50,4 +50,11 @@ public interface BpmProcessInstanceCopyService {
PageResult<BpmProcessInstanceCopyDO> getProcessInstanceCopyPage(Long userId,
BpmProcessInstanceCopyPageReqVO pageReqVO);
/**
*
*
* @param processInstanceId ID
*/
void deleteProcessInstanceCopy(String processInstanceId);
}

View File

@ -87,4 +87,9 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
return processInstanceCopyMapper.selectPage(userId, pageReqVO);
}
@Override
public void deleteProcessInstanceCopy(String processInstanceId) {
processInstanceCopyMapper.deleteByProcessInstanceId(processInstanceId);
}
}

View File

@ -195,8 +195,9 @@ public interface BpmTaskService {
*
*
* @param processInstanceId
* @param reason
*/
void moveTaskToEnd(String processInstanceId);
void moveTaskToEnd(String processInstanceId, String reason);
/**
* 退 targetDefinitionKey
@ -275,4 +276,12 @@ public interface BpmTaskService {
*/
void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType);
/**
*
*
* @param processInstanceId
* @param taskDefineKey Key
*/
void processDelayTimerTimeout(String processInstanceId, String taskDefineKey);
}

View File

@ -13,6 +13,7 @@ import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.enums.definition.*;
import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
@ -31,15 +32,13 @@ import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.bpmn.model.*;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
@ -63,6 +62,8 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseReasonRequire;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseSignEnable;
/**
* Service
@ -160,13 +161,18 @@ public class BpmTaskServiceImpl implements BpmTaskService {
BpmnModel bpmnModel = bpmProcessDefinitionService.getProcessDefinitionBpmnModel(todoTask.getProcessDefinitionId());
Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonsSetting = BpmnModelUtils.parseButtonsSetting(
bpmnModel, todoTask.getTaskDefinitionKey());
Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey());
Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey());
// 4. 任务表单
BpmFormDO taskForm = null;
if (StrUtil.isNotBlank(todoTask.getFormKey())){
if (StrUtil.isNotBlank(todoTask.getFormKey())) {
taskForm = formService.getForm(NumberUtils.parseLong(todoTask.getFormKey()));
}
return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm);
return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm)
.setSignEnable(signEnable)
.setReasonRequire(reasonRequire);
}
@Override
@ -477,6 +483,17 @@ public class BpmTaskServiceImpl implements BpmTaskService {
if (instance == null) {
throw exception(PROCESS_INSTANCE_NOT_EXISTS);
}
// 1.3 校验签名
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
Boolean signEnable = parseSignEnable(bpmnModel, task.getTaskDefinitionKey());
if (signEnable && StrUtil.isEmpty(reqVO.getSignPicUrl())) {
throw exception(TASK_SIGNATURE_NOT_EXISTS);
}
// 1.4 校验审批意见
Boolean reasonRequire = parseReasonRequire(bpmnModel, task.getTaskDefinitionKey());
if (reasonRequire && StrUtil.isEmpty(reqVO.getReason())) {
throw exception(TASK_REASON_REQUIRE);
}
// 情况一:被委派的任务,不调用 complete 去完成任务
if (DelegationState.PENDING.equals(task.getDelegationState())) {
@ -491,8 +508,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
}
// 情况三:审批普通的任务。大多数情况下,都是这样
// 2.1 更新 task 状态、原因
// 2.1 更新 task 状态、原因、签字
updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason());
if (signEnable) {
taskService.setVariableLocal(task.getId(), BpmnVariableConstants.TASK_SIGN_PIC_URL, reqVO.getSignPicUrl());
}
// 2.2 添加评论
taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
@ -637,8 +657,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
// 3.1 情况一:驳回到指定的任务节点
BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(userTaskElement);
if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) {
BpmUserTaskRejectHandlerTypeEnum userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(userTaskElement);
if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerTypeEnum.RETURN_USER_TASK) {
String returnTaskId = BpmnModelUtils.parseReturnTaskId(userTaskElement);
Assert.notNull(returnTaskId, "退回的节点不能为空");
returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId())
@ -647,7 +667,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
}
// 3.2 情况二:直接结束,审批不通过
processInstanceService.updateProcessInstanceReject(instance, reqVO.getReason()); // 标记不通过
moveTaskToEnd(task.getProcessInstanceId()); // 结束流程
moveTaskToEnd(task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); // 结束流程
}
/**
@ -818,7 +838,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
}
@Override
public void moveTaskToEnd(String processInstanceId) {
public void moveTaskToEnd(String processInstanceId, String reason) {
List<Task> taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null);
if (CollUtil.isEmpty(taskList)) {
return;
@ -844,6 +864,14 @@ public class BpmTaskServiceImpl implements BpmTaskService {
.processInstanceId(processInstanceId)
.moveActivityIdsToSingleActivityId(activityIds, endEvent.getId())
.changeState();
// 3. 特殊:如果跳转到 EndEvent 流程还未结束, 执行 deleteProcessInstance 方法
// TODO 芋艿:目前发现并行分支情况下,会存在这个情况,后续看看有没更好的方案;
List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();
if (CollUtil.isNotEmpty(executions)) {
log.warn("[moveTaskToEnd][执行跳转到 EndEvent 后, 流程实例未结束,强制执行 deleteProcessInstance 方法]");
runtimeService.deleteProcessInstance(processInstanceId, reason);
}
}
@Override
@ -1144,6 +1172,42 @@ public class BpmTaskServiceImpl implements BpmTaskService {
log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId());
return;
}
// 自动去重,通过自动审批的方式 TODO @芋艿 驳回的情况得考虑一下;@lesan驳回后又自动审批么
BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(task.getProcessDefinitionId());
if (processDefinitionInfo == null) {
log.error("[processTaskAssigned][taskId({}) 没有找到流程定义({})]", task.getId(), task.getProcessDefinitionId());
return;
}
if (processDefinitionInfo.getAutoApprovalType() != null) {
HistoricTaskInstanceQuery sameAssigneeQuery = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(task.getProcessInstanceId())
.taskAssignee(task.getAssignee()) // 相同审批人
.taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.APPROVE.getStatus())
.finished();
if (BpmAutoApproveTypeEnum.APPROVE_ALL.getType().equals(processDefinitionInfo.getAutoApprovalType())
&& sameAssigneeQuery.count() > 0) {
getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
.setReason(BpmAutoApproveTypeEnum.APPROVE_ALL.getName()));
return;
}
if (BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getType().equals(processDefinitionInfo.getAutoApprovalType())) {
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
if (bpmnModel == null) {
log.error("[processTaskAssigned][taskId({}) 没有找到流程模型({})]", task.getId(), task.getProcessDefinitionId());
return;
}
List<String> sourceTaskIds = convertList(BpmnModelUtils.getElementIncomingFlows( // 获取所有上一个节点
BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey())),
SequenceFlow::getSourceRef);
if (sameAssigneeQuery.taskDefinitionKeys(sourceTaskIds).count() > 0) {
getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
.setReason(BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getName()));
return;
}
}
}
// 审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理
if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
// 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略
@ -1191,9 +1255,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
}
}
}
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())).getCheckedData();
messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
// 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号
FlowableUtils.execute(processInstance.getTenantId(), () -> {
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())).getCheckedData();
messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
});
}
});
@ -1238,6 +1304,23 @@ public class BpmTaskServiceImpl implements BpmTaskService {
}));
}
@Override
public void processDelayTimerTimeout(String processInstanceId, String taskDefineKey) {
Execution execution = runtimeService.createExecutionQuery()
.processInstanceId(processInstanceId)
.activityId(taskDefineKey)
.singleResult();
if (execution == null) {
log.error("[processDelayTimerTimeout][processInstanceId({}) activityId({}) 没有找到执行活动]",
processInstanceId, taskDefineKey);
return;
}
// 若存在直接触发接收任务,执行后续节点
FlowableUtils.execute(execution.getTenantId(),
() -> runtimeService.trigger(execution.getId()));
}
/**
* AOP
*

View File

@ -0,0 +1,96 @@
package cn.iocoder.yudao.module.bpm.service.task.listener;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import jakarta.annotation.Resource;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.el.FixedValue;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseListenerConfig;
// TODO @芋艿:可能会想换个包地址
/**
* BPM
*
* @author Lesan
*/
@Component
@Slf4j
@Scope("prototype")
public class BpmUserTaskListener implements TaskListener {
public static final String DELEGATE_EXPRESSION = "${bpmUserTaskListener}";
@Resource
private BpmProcessInstanceService processInstanceService;
@Resource
private RestTemplate restTemplate;
@Setter
private FixedValue listenerConfig;
@Override
public void notify(DelegateTask delegateTask) {
// 1. 获取所需基础信息
HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(delegateTask.getProcessInstanceId());
BpmSimpleModelNodeVO.ListenerHandler listenerHandler = parseListenerConfig(listenerConfig);
// 2. 获取请求头和请求体
Map<String, Object> processVariables = processInstance.getProcessVariables();
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
SimpleModelUtils.addHttpRequestParam(headers, listenerHandler.getHeader(), processVariables);
SimpleModelUtils.addHttpRequestParam(body, listenerHandler.getBody(), processVariables);
// 2.1 请求头默认参数
if (StrUtil.isNotEmpty(delegateTask.getTenantId())) {
headers.add(HEADER_TENANT_ID, delegateTask.getTenantId());
}
// 2.2 请求体默认参数
// TODO @芋艿:哪些默认参数,后续再调研下;感觉可以搞个 task 字段,把整个 delegateTask 放进去;
body.add("processInstanceId", delegateTask.getProcessInstanceId());
body.add("assignee", delegateTask.getAssignee());
body.add("taskDefinitionKey", delegateTask.getTaskDefinitionKey());
body.add("taskId", delegateTask.getId());
// 3. 异步发起请求
// TODO @芋艿:确认要同步,还是异步
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
try {
ResponseEntity<String> responseEntity = restTemplate.exchange(listenerHandler.getPath(), HttpMethod.POST,
requestEntity, String.class);
log.info("[notify][监听器:{},事件类型:{},请求头:{},请求体:{},响应结果:{}]",
DELEGATE_EXPRESSION,
delegateTask.getEventName(),
headers,
body,
responseEntity);
} catch (RestClientException e) {
log.error("[error][监听器:{},事件类型:{},请求头:{},请求体:{},请求出错:{}]",
DELEGATE_EXPRESSION,
delegateTask.getEventName(),
headers,
body,
e.getMessage());
}
// 4. 是否需要后续操作TODO 芋艿:待定!
}
}

View File

@ -0,0 +1,75 @@
package cn.iocoder.yudao.module.bpm.service.task.trigger;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
/**
* BPM HTTP
*
* @author jason
*/
@Component
@Slf4j
public class BpmHttpRequestTrigger implements BpmTrigger {
@Resource
private BpmProcessInstanceService processInstanceService;
@Resource
private RestTemplate restTemplate;
@Override
public BpmTriggerTypeEnum getType() {
return BpmTriggerTypeEnum.HTTP_REQUEST;
}
@Override
public void execute(String processInstanceId, String param) {
// 1. 解析 http 请求配置
HttpRequestTriggerSetting setting = JsonUtils.parseObject(param, HttpRequestTriggerSetting.class);
if (setting == null) {
log.error("[execute][流程({}) HTTP 触发器请求配置为空]", processInstanceId);
return;
}
// 2.1 设置请求头
ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
Map<String, Object> processVariables = processInstance.getProcessVariables();
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add(HEADER_TENANT_ID, processInstance.getTenantId());
SimpleModelUtils.addHttpRequestParam(headers, setting.getHeader(), processVariables);
// 2.2 设置请求体
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
SimpleModelUtils.addHttpRequestParam(body, setting.getBody(), processVariables);
body.add("processInstanceId", processInstanceId);
// TODO @芋艿:要不要抽象一个 Http 请求的工具类,方便复用呢?
// 3. 发起请求
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
try {
ResponseEntity<String> responseEntity = restTemplate.exchange(setting.getUrl(), HttpMethod.POST,
requestEntity, String.class);
log.info("[execute][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity);
} catch (RestClientException e) {
log.error("[execute][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage());
}
}
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.bpm.service.task.trigger;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
// TODO @芋艿:可能会想换个包地址
/**
* BPM
* <p>
*
*
* @author jason
*/
public interface BpmTrigger {
/**
*
*
* @return
*/
BpmTriggerTypeEnum getType();
/**
*
*
* @param processInstanceId
* @param param
*/
void execute(String processInstanceId, String param);
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.crm.enums.business;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -13,13 +13,13 @@ import java.util.Arrays;
*/
@RequiredArgsConstructor
@Getter
public enum CrmBusinessEndStatusEnum implements IntArrayValuable {
public enum CrmBusinessEndStatusEnum implements ArrayValuable<Integer> {
WIN(1, "赢单"),
LOSE(2, "输单"),
INVALID(3, "无效");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmBusinessEndStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmBusinessEndStatusEnum::getStatus).toArray(Integer[]::new);
/**
*
@ -31,7 +31,7 @@ public enum CrmBusinessEndStatusEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.crm.enums.common;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@RequiredArgsConstructor
@Getter
public enum CrmAuditStatusEnum implements IntArrayValuable {
public enum CrmAuditStatusEnum implements ArrayValuable<Integer> {
DRAFT(0, "未提交"),
PROCESS(10, "审批中"),
@ -24,10 +24,10 @@ public enum CrmAuditStatusEnum implements IntArrayValuable {
private final Integer status;
private final String name;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmAuditStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmAuditStatusEnum::getStatus).toArray(Integer[]::new);
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.crm.enums.common;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -15,7 +15,7 @@ import java.util.Arrays;
*/
@RequiredArgsConstructor
@Getter
public enum CrmBizTypeEnum implements IntArrayValuable {
public enum CrmBizTypeEnum implements ArrayValuable<Integer> {
CRM_CLUE(1, "线索"),
CRM_CUSTOMER(2, "客户"),
@ -27,7 +27,7 @@ public enum CrmBizTypeEnum implements IntArrayValuable {
CRM_RECEIVABLE_PLAN(8, "回款计划")
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmBizTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmBizTypeEnum::getType).toArray(Integer[]::new);
/**
*
@ -45,7 +45,7 @@ public enum CrmBizTypeEnum implements IntArrayValuable {
}
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.crm.enums.common;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,13 +14,13 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum CrmSceneTypeEnum implements IntArrayValuable {
public enum CrmSceneTypeEnum implements ArrayValuable<Integer> {
OWNER(1, "我负责的"),
INVOLVED(2, "我参与的"),
SUBORDINATE(3, "下属负责的");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmSceneTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmSceneTypeEnum::getType).toArray(Integer[]::new);
/**
*
@ -44,7 +44,7 @@ public enum CrmSceneTypeEnum implements IntArrayValuable {
}
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.crm.enums.customer;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,13 +13,13 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum CrmCustomerLevelEnum implements IntArrayValuable {
public enum CrmCustomerLevelEnum implements ArrayValuable<Integer> {
IMPORTANT(1, "A重点客户"),
GENERAL(2, "B普通客户"),
LOW_PRIORITY(3, "C非优先客户");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerLevelEnum::getLevel).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmCustomerLevelEnum::getLevel).toArray(Integer[]::new);
/**
*
@ -31,7 +31,7 @@ public enum CrmCustomerLevelEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.crm.enums.customer;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -15,7 +15,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum CrmCustomerLimitConfigTypeEnum implements IntArrayValuable {
public enum CrmCustomerLimitConfigTypeEnum implements ArrayValuable<Integer> {
/**
*
@ -27,7 +27,7 @@ public enum CrmCustomerLimitConfigTypeEnum implements IntArrayValuable {
CUSTOMER_LOCK_LIMIT(2, "锁定客户数限制"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerLimitConfigTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmCustomerLimitConfigTypeEnum::getType).toArray(Integer[]::new);
/**
*
@ -45,7 +45,7 @@ public enum CrmCustomerLimitConfigTypeEnum implements IntArrayValuable {
}
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.crm.enums.permission;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -17,13 +17,13 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum CrmPermissionLevelEnum implements IntArrayValuable {
public enum CrmPermissionLevelEnum implements ArrayValuable<Integer> {
OWNER(1, "负责人"),
READ(2, "只读"),
WRITE(3, "读写");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmPermissionLevelEnum::getLevel).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmPermissionLevelEnum::getLevel).toArray(Integer[]::new);
/**
*
@ -35,7 +35,7 @@ public enum CrmPermissionLevelEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.crm.enums.product;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -15,12 +15,12 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum CrmProductStatusEnum implements IntArrayValuable {
public enum CrmProductStatusEnum implements ArrayValuable<Integer> {
DISABLE(0, "下架"),
ENABLE(1, "上架");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmProductStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmProductStatusEnum::getStatus).toArray(Integer[]::new);
/**
*
@ -32,7 +32,7 @@ public enum CrmProductStatusEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.crm.enums.receivable;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum CrmReceivableReturnTypeEnum implements IntArrayValuable {
public enum CrmReceivableReturnTypeEnum implements ArrayValuable<Integer> {
CHECK(1, "支票"),
CASH(2, "现金"),
@ -24,7 +24,7 @@ public enum CrmReceivableReturnTypeEnum implements IntArrayValuable {
WECHAT_PAY(7, "微信支付"),
OTHER(8, "其它");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmReceivableReturnTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CrmReceivableReturnTypeEnum::getType).toArray(Integer[]::new);
/**
*
@ -36,7 +36,7 @@ public enum CrmReceivableReturnTypeEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.erp.enums;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -15,12 +15,12 @@ import java.util.Arrays;
*/
@RequiredArgsConstructor
@Getter
public enum ErpAuditStatus implements IntArrayValuable {
public enum ErpAuditStatus implements ArrayValuable<Integer> {
PROCESS(10, "未审核"), // 审核中
APPROVE(20, "已审核"); // 审核通过
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ErpAuditStatus::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpAuditStatus::getStatus).toArray(Integer[]::new);
/**
*
@ -32,7 +32,7 @@ public enum ErpAuditStatus implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.erp.enums.common;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@RequiredArgsConstructor
@Getter
public enum ErpBizTypeEnum implements IntArrayValuable {
public enum ErpBizTypeEnum implements ArrayValuable<Integer> {
PURCHASE_ORDER(10, "采购订单"),
PURCHASE_IN(11, "采购入库"),
@ -24,7 +24,7 @@ public enum ErpBizTypeEnum implements IntArrayValuable {
SALE_RETURN(22, "销售退货"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ErpBizTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpBizTypeEnum::getType).toArray(Integer[]::new);
/**
*
@ -36,7 +36,7 @@ public enum ErpBizTypeEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.erp.enums.stock;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@RequiredArgsConstructor
@Getter
public enum ErpStockRecordBizTypeEnum implements IntArrayValuable {
public enum ErpStockRecordBizTypeEnum implements ArrayValuable<Integer> {
OTHER_IN(10, "其它入库"),
OTHER_IN_CANCEL(11, "其它入库(作废)"),
@ -44,7 +44,7 @@ public enum ErpStockRecordBizTypeEnum implements IntArrayValuable {
PURCHASE_RETURN_CANCEL(81, "采购退货出库(作废)"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ErpStockRecordBizTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpStockRecordBizTypeEnum::getType).toArray(Integer[]::new);
/**
*
@ -56,7 +56,7 @@ public enum ErpStockRecordBizTypeEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.product.enums.comment;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,13 +13,13 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum ProductCommentAuditStatusEnum implements IntArrayValuable {
public enum ProductCommentAuditStatusEnum implements ArrayValuable<Integer> {
NONE(0, "待审核"),
APPROVE(1, "审批通过"),
REJECT(2, "审批不通过"),;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductCommentAuditStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(ProductCommentAuditStatusEnum::getStatus).toArray(Integer[]::new);
/**
*
@ -31,7 +31,7 @@ public enum ProductCommentAuditStatusEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.product.enums.comment;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum ProductCommentScoresEnum implements IntArrayValuable {
public enum ProductCommentScoresEnum implements ArrayValuable<Integer> {
ONE(1, "1星"),
TWO(2, "2星"),
@ -21,7 +21,7 @@ public enum ProductCommentScoresEnum implements IntArrayValuable {
FOUR(4, "4星"),
FIVE(5, "5星");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductCommentScoresEnum::getScores).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(ProductCommentScoresEnum::getScores).toArray(Integer[]::new);
/**
*
@ -34,7 +34,7 @@ public enum ProductCommentScoresEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.product.enums.spu;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,13 +13,13 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum ProductSpuStatusEnum implements IntArrayValuable {
public enum ProductSpuStatusEnum implements ArrayValuable<Integer> {
RECYCLE(-1, "回收站"),
DISABLE(0, "下架"),
ENABLE(1, "上架");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductSpuStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(ProductSpuStatusEnum::getStatus).toArray(Integer[]::new);
/**
*
@ -31,7 +31,7 @@ public enum ProductSpuStatusEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.promotion.enums.banner;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum BannerPositionEnum implements IntArrayValuable {
public enum BannerPositionEnum implements ArrayValuable<Integer> {
HOME_POSITION(1, "首页"),
SECKILL_POSITION(2, "秒杀活动页"),
@ -21,7 +21,7 @@ public enum BannerPositionEnum implements IntArrayValuable {
DISCOUNT_POSITION(4, "限时折扣页"),
REWARD_POSITION(5, "满减送页");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BannerPositionEnum::getPosition).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BannerPositionEnum::getPosition).toArray(Integer[]::new);
/**
*
@ -33,7 +33,7 @@ public enum BannerPositionEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.promotion.enums.bargain;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,14 +13,14 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum BargainRecordStatusEnum implements IntArrayValuable {
public enum BargainRecordStatusEnum implements ArrayValuable<Integer> {
IN_PROGRESS(1, "砍价中"),
SUCCESS(2, "砍价成功"),
FAILED(3, "砍价失败"), // 活动到期时,会自动将到期的砍价全部设置为过期
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BargainRecordStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BargainRecordStatusEnum::getStatus).toArray(Integer[]::new);
/**
*
@ -32,7 +32,7 @@ public enum BargainRecordStatusEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.promotion.enums.combination;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,13 +14,13 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum CombinationRecordStatusEnum implements IntArrayValuable {
public enum CombinationRecordStatusEnum implements ArrayValuable<Integer> {
IN_PROGRESS(0, "进行中"),
SUCCESS(1, "拼团成功"),
FAILED(2, "拼团失败");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CombinationRecordStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CombinationRecordStatusEnum::getStatus).toArray(Integer[]::new);
/**
*
@ -32,7 +32,7 @@ public enum CombinationRecordStatusEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.promotion.enums.common;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,14 +14,14 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum PromotionActivityStatusEnum implements IntArrayValuable {
public enum PromotionActivityStatusEnum implements ArrayValuable<Integer> {
WAIT(10, "未开始"),
RUN(20, "进行中"),
END(30, "已结束"),
CLOSE(40, "已关闭");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionActivityStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(PromotionActivityStatusEnum::getStatus).toArray(Integer[]::new);
/**
*
@ -33,7 +33,7 @@ public enum PromotionActivityStatusEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.promotion.enums.common;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,12 +13,12 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum PromotionConditionTypeEnum implements IntArrayValuable {
public enum PromotionConditionTypeEnum implements ArrayValuable<Integer> {
PRICE(10, "满 N 元"),
COUNT(20, "满 N 件");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionConditionTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(PromotionConditionTypeEnum::getType).toArray(Integer[]::new);
/**
*
@ -30,7 +30,7 @@ public enum PromotionConditionTypeEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.promotion.enums.common;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,13 +13,13 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum PromotionDiscountTypeEnum implements IntArrayValuable {
public enum PromotionDiscountTypeEnum implements ArrayValuable<Integer> {
PRICE(1, "满减"), // 具体金额
PERCENT(2, "折扣"), // 百分比
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionDiscountTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(PromotionDiscountTypeEnum::getType).toArray(Integer[]::new);
/**
*
@ -31,7 +31,7 @@ public enum PromotionDiscountTypeEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.promotion.enums.common;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,13 +14,13 @@ import java.util.Objects;
*/
@Getter
@AllArgsConstructor
public enum PromotionProductScopeEnum implements IntArrayValuable {
public enum PromotionProductScopeEnum implements ArrayValuable<Integer> {
ALL(1, "全部商品"),
SPU(2, "指定商品"),
CATEGORY(3, "指定品类");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionProductScopeEnum::getScope).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(PromotionProductScopeEnum::getScope).toArray(Integer[]::new);
/**
*
@ -32,7 +32,7 @@ public enum PromotionProductScopeEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.promotion.enums.common;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum PromotionTypeEnum implements IntArrayValuable {
public enum PromotionTypeEnum implements ArrayValuable<Integer> {
SECKILL_ACTIVITY(1, "秒杀活动"),
BARGAIN_ACTIVITY(2, "砍价活动"),
@ -27,7 +27,7 @@ public enum PromotionTypeEnum implements IntArrayValuable {
POINT(8, "积分")
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(PromotionTypeEnum::getType).toArray(Integer[]::new);
/**
*
@ -39,7 +39,7 @@ public enum PromotionTypeEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

Some files were not shown because too many files have changed in this diff Show More