Compare commits
149 Commits
v2.4.0(jdk
...
v2.4.1(jdk
Author | SHA1 | Date | |
---|---|---|---|
![]() |
542beaf47b | ||
![]() |
2d18f593ae | ||
![]() |
cf7ff1ca8a | ||
![]() |
d75d71d7d0 | ||
![]() |
949fa22509 | ||
![]() |
19b5582a78 | ||
![]() |
4e9583fae4 | ||
![]() |
b00d22a907 | ||
![]() |
129e9868fb | ||
![]() |
3cbe59ace3 | ||
![]() |
9569c3fbed | ||
![]() |
f5050807e1 | ||
![]() |
a23b0480f1 | ||
![]() |
4e43958fe3 | ||
![]() |
b4f93e832f | ||
![]() |
362b872aa8 | ||
![]() |
44486afd95 | ||
![]() |
f47f6d934e | ||
![]() |
f8d6f1e2c4 | ||
![]() |
0723e4571d | ||
![]() |
53789d9b80 | ||
![]() |
805e570406 | ||
![]() |
86f568280a | ||
![]() |
8f16786471 | ||
![]() |
b8e56a6bde | ||
![]() |
86dc3763fc | ||
![]() |
623a41d48c | ||
![]() |
1ae51bde20 | ||
![]() |
3342ae1be8 | ||
![]() |
69efe91bde | ||
![]() |
f409a67d2f | ||
![]() |
0ef8467546 | ||
![]() |
f60a4dfa6b | ||
![]() |
06634a4265 | ||
![]() |
534c64709f | ||
![]() |
d63e315876 | ||
![]() |
bb798681f7 | ||
![]() |
0e07da807a | ||
![]() |
295fad7d97 | ||
![]() |
a78e536906 | ||
![]() |
a33fb12799 | ||
![]() |
9d9351d066 | ||
![]() |
d11684c2ae | ||
![]() |
d83b7cd5b9 | ||
![]() |
39e68af120 | ||
![]() |
b5ba500b60 | ||
![]() |
8f4543a270 | ||
![]() |
bcfa08f8ef | ||
![]() |
408f88da54 | ||
![]() |
bd7f9761d8 | ||
![]() |
846d0605d8 | ||
![]() |
281d3d1d53 | ||
![]() |
dcf55c3533 | ||
![]() |
a0ff1244e5 | ||
![]() |
e99eb5c813 | ||
![]() |
2ceca1a20d | ||
![]() |
e89e3c946d | ||
![]() |
de297e3a78 | ||
![]() |
30a24601cd | ||
![]() |
d3352308d4 | ||
![]() |
61e3275231 | ||
![]() |
808f23ec49 | ||
![]() |
8a8dc67d72 | ||
![]() |
e548d4454e | ||
![]() |
6317a4f361 | ||
![]() |
ca1d9e6896 | ||
![]() |
c55f77f001 | ||
![]() |
3ec5b19a77 | ||
![]() |
0642c5ebe4 | ||
![]() |
d8bc3a46e5 | ||
![]() |
4a1fe1f307 | ||
![]() |
f486790def | ||
![]() |
4afdcb4699 | ||
![]() |
daa718a444 | ||
![]() |
c4fcd0564c | ||
![]() |
5c5448b959 | ||
![]() |
e55be75357 | ||
![]() |
d36fc98f01 | ||
![]() |
96875b1e02 | ||
![]() |
081e716e72 | ||
![]() |
89728fe6ff | ||
![]() |
fcf0b562fd | ||
![]() |
480ed8db80 | ||
![]() |
68b7a9a954 | ||
![]() |
d758da489c | ||
![]() |
6c3afa7c96 | ||
![]() |
2725376b91 | ||
![]() |
5c2af8e32d | ||
![]() |
25c86c05b3 | ||
![]() |
a985ba923e | ||
![]() |
70538320c8 | ||
![]() |
a16f1120e0 | ||
![]() |
bcc535e034 | ||
![]() |
8f994af718 | ||
![]() |
9c4648e545 | ||
![]() |
40082c9b49 | ||
![]() |
62b271fbf8 | ||
![]() |
93eb75552a | ||
![]() |
1784aab186 | ||
![]() |
610f39de05 | ||
![]() |
aba9626384 | ||
![]() |
31c2e81dd2 | ||
![]() |
56f3177a81 | ||
![]() |
2451942cf2 | ||
![]() |
b1c115d221 | ||
![]() |
b2d81e0763 | ||
![]() |
71b86e6207 | ||
![]() |
267435c90f | ||
![]() |
f1296c59eb | ||
![]() |
533e5c3bf5 | ||
![]() |
a85d51cc8c | ||
![]() |
343991445f | ||
![]() |
37cb20d964 | ||
![]() |
fcbf1d0765 | ||
![]() |
62aabc633c | ||
![]() |
adc05cad66 | ||
![]() |
fd4b6ef9e4 | ||
![]() |
1fb8c9238a | ||
![]() |
60d1f1199d | ||
![]() |
735ec9e3ac | ||
![]() |
4b09bff64b | ||
![]() |
4acb01c3b0 | ||
![]() |
d33afd7a9a | ||
![]() |
a008b313ec | ||
![]() |
ae3c63eb29 | ||
![]() |
da9f6ab76e | ||
![]() |
adff185716 | ||
![]() |
955ad86db4 | ||
![]() |
34c8f4cae1 | ||
![]() |
b5f6545d5a | ||
![]() |
fdc6f6ed65 | ||
![]() |
1d597403c9 | ||
![]() |
46c125b030 | ||
![]() |
a13b582009 | ||
![]() |
8b91b471d5 | ||
![]() |
44bba85a34 | ||
![]() |
92a4831644 | ||
![]() |
0e9827e5a0 | ||
![]() |
39ac9053fd | ||
![]() |
92d6b8ad88 | ||
![]() |
6a7e7e3e44 | ||
![]() |
f8fc3101c6 | ||
![]() |
8d22ed8bea | ||
![]() |
182511b65b | ||
![]() |
686a64ccda | ||
![]() |
9512dcf812 | ||
![]() |
e380bc34f3 | ||
![]() |
b48052a5f5 | ||
![]() |
4c8b83d46f |
2
pom.xml
2
pom.xml
@@ -32,7 +32,7 @@
|
||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<properties>
|
||||
<revision>2.4.0-SNAPSHOT</revision>
|
||||
<revision>2.4.1-SNAPSHOT</revision>
|
||||
<!-- Maven 相关 -->
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
|
@@ -2,16 +2,16 @@
|
||||
"local": {
|
||||
"baseUrl": "http://127.0.0.1:48080/admin-api",
|
||||
"token": "test1",
|
||||
"adminTenentId": "1",
|
||||
"adminTenantId": "1",
|
||||
|
||||
"appApi": "http://127.0.0.1:48080/app-api",
|
||||
"appToken": "test247",
|
||||
"appTenentId": "1"
|
||||
"appTenantId": "1"
|
||||
},
|
||||
"gateway": {
|
||||
"baseUrl": "http://127.0.0.1:8888/admin-api",
|
||||
"token": "test1",
|
||||
"adminTenentId": "1",
|
||||
"adminTenantId": "1",
|
||||
|
||||
"appApi": "http://127.0.0.1:8888/app-api",
|
||||
"appToken": "test1",
|
||||
|
@@ -14,7 +14,7 @@
|
||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<properties>
|
||||
<revision>2.4.0-SNAPSHOT</revision>
|
||||
<revision>2.4.1-SNAPSHOT</revision>
|
||||
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
|
||||
<!-- 统一依赖管理 -->
|
||||
<spring.boot.version>3.4.1</spring.boot.version>
|
||||
|
@@ -0,0 +1,15 @@
|
||||
package cn.iocoder.yudao.framework.common.core;
|
||||
|
||||
/**
|
||||
* 可生成 T 数组的接口
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public interface ArrayValuable<T> {
|
||||
|
||||
/**
|
||||
* @return 数组
|
||||
*/
|
||||
T[] array();
|
||||
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.common.core;
|
||||
|
||||
/**
|
||||
* 可生成 Int 数组的接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IntArrayValuable {
|
||||
|
||||
/**
|
||||
* @return int 数组
|
||||
*/
|
||||
int[] array();
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -199,4 +199,12 @@ public class JsonUtils {
|
||||
return JSONUtil.isTypeJSON(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否为 JSON 类型的字符串
|
||||
* @param str 字符串
|
||||
*/
|
||||
public static boolean isJsonObject(String str) {
|
||||
return JSONUtil.isTypeJSONObject(str);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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 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}";
|
||||
|
||||
|
@@ -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 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(); // 重新添加错误提示语句
|
||||
|
@@ -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 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(); // 重新添加错误提示语句
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -128,7 +128,7 @@ public class YudaoWebSecurityConfigurerAdapter {
|
||||
// ①:全局共享规则
|
||||
.authorizeHttpRequests(c -> c
|
||||
// 1.1 静态资源,可匿名访问
|
||||
.requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll()
|
||||
.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()
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -20,8 +20,8 @@ public abstract class ApiRequestFilter extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected boolean shouldNotFilter(HttpServletRequest request) {
|
||||
// 只过滤 API 请求的地址
|
||||
return !StrUtil.startWithAny(request.getRequestURI(), webProperties.getAdminApi().getPrefix(),
|
||||
webProperties.getAppApi().getPrefix());
|
||||
String apiUri = request.getRequestURI().substring(request.getContextPath().length());
|
||||
return !StrUtil.startWithAny(apiUri, webProperties.getAdminApi().getPrefix(), webProperties.getAppApi().getPrefix());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
POST {{baseUrl}}/ai/chat/message/send
|
||||
Content-Type: application/json
|
||||
Authorization: {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
tenant-id: {{adminTenantId}}
|
||||
|
||||
{
|
||||
"conversationId": "1781604279872581724",
|
||||
@@ -13,7 +13,7 @@ tenant-id: {{adminTenentId}}
|
||||
POST {{baseUrl}}/ai/chat/message/send-stream
|
||||
Content-Type: application/json
|
||||
Authorization: {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
tenant-id: {{adminTenantId}}
|
||||
|
||||
{
|
||||
"conversationId": "1781604279872581724",
|
||||
|
@@ -57,6 +57,17 @@
|
||||
<groupId>${spring-ai.groupId}</groupId>
|
||||
<artifactId>spring-ai-tika-document-reader</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
<!-- TODO 芋艿:boot 项目里,不引入 cloud 依赖!!!另外,这样也是为了解决启动报错的问题! -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>spring-cloud-function-context</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>spring-cloud-function-core</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${spring-ai.groupId}</groupId>
|
||||
|
@@ -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, "动态表单不存在");
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -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());
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -10,7 +10,7 @@ import lombok.Getter;
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum BpmProcessListenerType {
|
||||
public enum BpmProcessListenerTypeEnum {
|
||||
|
||||
EXECUTION("execution", "执行监听器"),
|
||||
TASK("task", "任务执行器");
|
@@ -10,7 +10,7 @@ import lombok.Getter;
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum BpmProcessListenerValueType {
|
||||
public enum BpmProcessListenerValueTypeEnum {
|
||||
|
||||
CLASS("class", "Java 类"),
|
||||
DELEGATE_EXPRESSION("delegateExpression", "代理表达式"),
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
@@ -0,0 +1,43 @@
|
||||
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 请求"),
|
||||
UPDATE_NORMAL_FORM(2, "更新流程表单"); // TODO @jason:FORM_UPDATE
|
||||
|
||||
/**
|
||||
* 触发器执行动作类型
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -32,6 +32,7 @@ public enum BpmReasonEnum {
|
||||
ASSIGN_EMPTY_REJECT("审批人为空,自动不通过"),
|
||||
APPROVE_TYPE_AUTO_APPROVE("非人工审核,自动通过"),
|
||||
APPROVE_TYPE_AUTO_REJECT("非人工审核,自动不通过"),
|
||||
CANCEL_BY_PROCESS_CLEAN("进程清理自动取消"),
|
||||
;
|
||||
|
||||
private final String reason;
|
||||
|
@@ -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")
|
||||
|
@@ -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;
|
||||
|
||||
}
|
@@ -1,9 +1,12 @@
|
||||
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
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;
|
||||
@@ -62,4 +65,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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.*;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||
@@ -9,6 +10,7 @@ import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -24,7 +26,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 +38,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 +62,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 +87,86 @@ 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 +193,6 @@ public class BpmSimpleModelNodeVO {
|
||||
|
||||
@Schema(description = "最大提醒次数", example = "1")
|
||||
private Integer maxRemindCount;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "空处理策略")
|
||||
@@ -143,7 +207,6 @@ public class BpmSimpleModelNodeVO {
|
||||
|
||||
@Schema(description = "指定人员审批的用户编号数组", example = "1")
|
||||
private List<Long> userIds;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "操作按钮设置")
|
||||
@@ -162,6 +225,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 +293,102 @@ 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;
|
||||
|
||||
// TODO @jason:这个要不直接叫 formSetting,更好理解一点哈
|
||||
// TODO @jason:如果搞成 List<NormalFormTriggerSetting>,是不是可以做条件组了?微信讨论哈
|
||||
/**
|
||||
* 流程表单触发器设置
|
||||
*/
|
||||
private NormalFormTriggerSetting normalFormSetting;
|
||||
|
||||
@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;
|
||||
|
||||
// TODO @json:可能未来看情况,搞个 HttpResponseParam;得看看有没别的业务需要,抽象统一
|
||||
/**
|
||||
* 请求返回处理设置,用于修改流程表单值
|
||||
* <p>
|
||||
* key:表示要修改的流程表单字段名(name)
|
||||
* value:接口返回的字段名
|
||||
*/
|
||||
@Schema(description = "请求返回处理设置", example = "[]")
|
||||
private List<KeyValue<String, String>> response;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "流程表单触发器设置", example = "{}")
|
||||
@Data
|
||||
public static class NormalFormTriggerSetting {
|
||||
|
||||
@Schema(description = "修改的表单字段", example = "userName")
|
||||
private Map<String, Object> updateFormFields;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -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")
|
||||
|
@@ -9,7 +9,10 @@ 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.cc.BpmProcessInstanceCopyRespVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
@@ -42,6 +45,8 @@ public class BpmProcessInstanceCopyController {
|
||||
private BpmProcessInstanceCopyService processInstanceCopyService;
|
||||
@Resource
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
@Resource
|
||||
private BpmProcessDefinitionService processDefinitionService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@@ -62,6 +67,8 @@ public class BpmProcessInstanceCopyController {
|
||||
convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId));
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),
|
||||
copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator()))));
|
||||
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
|
||||
convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessDefinitionId));
|
||||
return success(convertPage(pageResult, copy -> {
|
||||
BpmProcessInstanceCopyRespVO copyVO = BeanUtils.toBean(copy, BpmProcessInstanceCopyRespVO.class);
|
||||
MapUtils.findAndThen(userMap, Long.valueOf(copy.getCreator()),
|
||||
@@ -69,7 +76,12 @@ public class BpmProcessInstanceCopyController {
|
||||
MapUtils.findAndThen(userMap, copy.getStartUserId(),
|
||||
user -> copyVO.setCreateUser(BeanUtils.toBean(user, UserSimpleBaseVO.class)));
|
||||
MapUtils.findAndThen(processInstanceMap, copyVO.getProcessInstanceId(),
|
||||
processInstance -> copyVO.setProcessInstanceStartTime(DateUtils.of(processInstance.getStartTime())));
|
||||
processInstance -> {
|
||||
copyVO.setSummary(FlowableUtils.getSummary(
|
||||
processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),
|
||||
processInstance.getProcessVariables()));
|
||||
copyVO.setProcessInstanceStartTime(DateUtils.of(processInstance.getStartTime()));
|
||||
});
|
||||
return copyVO;
|
||||
}));
|
||||
}
|
||||
|
@@ -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")
|
||||
|
@@ -1,10 +1,12 @@
|
||||
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 流程实例抄送的分页 Item Response VO")
|
||||
@Data
|
||||
@@ -40,4 +42,7 @@ public class BpmProcessInstanceCopyRespVO {
|
||||
@Schema(description = "抄送时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "流程摘要", example = "[]")
|
||||
private List<KeyValue<String, String>> summary;
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -32,7 +32,14 @@ public class BpmProcessInstancePageReqVO extends PageParam {
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
@Schema(description = "结束时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] endTime;
|
||||
|
||||
@Schema(description = "发起用户编号", example = "1024")
|
||||
private Long startUserId; // 注意,只有在【流程实例】菜单,才使用该参数
|
||||
|
||||
@Schema(description = "动态表单字段查询 JSON Str", example = "{}")
|
||||
private String formFieldsParams; // SpringMVC 在 get 请求下,无法方便的定义 Map 类型的参数,所以通过 String 接收后,逻辑里面转换
|
||||
|
||||
}
|
||||
|
@@ -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 = "请假")
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -14,7 +14,6 @@ public class BpmTaskRejectReqVO {
|
||||
private String id;
|
||||
|
||||
@Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!")
|
||||
@NotEmpty(message = "审批意见不能为空")
|
||||
private String reason;
|
||||
|
||||
}
|
||||
|
@@ -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,12 @@ public class BpmTaskRespVO {
|
||||
@Schema(description = "操作按钮设置值")
|
||||
private Map<Integer, OperationButtonSetting> buttonsSetting;
|
||||
|
||||
@Schema(description = "是否需要签名", example = "false")
|
||||
private Boolean signEnable;
|
||||
|
||||
@Schema(description = "是否填写审批意见", example = "false")
|
||||
private Boolean reasonRequire;
|
||||
|
||||
@Data
|
||||
@Schema(description = "流程实例")
|
||||
public static class ProcessInstance {
|
||||
@@ -94,6 +101,9 @@ public class BpmTaskRespVO {
|
||||
@Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private String processDefinitionId;
|
||||
|
||||
@Schema(description = "流程摘要", example = "[]")
|
||||
private List<KeyValue<String, String>> summary; // 只有流程表单,才有摘要!
|
||||
|
||||
/**
|
||||
* 发起人的用户信息
|
||||
*/
|
||||
|
@@ -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,11 @@ 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()));
|
||||
// 表单
|
||||
respVO.setFormVariables(pageResult.getList().get(i).getProcessVariables());
|
||||
}
|
||||
return vpPageResult;
|
||||
}
|
||||
@@ -186,7 +192,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,
|
||||
|
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.convert.task;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
@@ -9,6 +10,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 +43,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 +53,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.getProcessInstance().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 +79,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.getProcessInstance().setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),
|
||||
processInstance.getProcessVariables()));
|
||||
}
|
||||
return taskVO;
|
||||
});
|
||||
|
@@ -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;
|
||||
|
||||
}
|
||||
|
@@ -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. execution:ExecutionListener <a href="https://tkjohn.github.io/flowable-userguide/#executionListeners">执行监听器</a>
|
||||
* 2. task:TaskListener <a href="https://tkjohn.github.io/flowable-userguide/#taskListeners">任务监听器</a>
|
||||
|
@@ -49,6 +49,12 @@ public class BpmProcessInstanceCopyDO extends BaseDO {
|
||||
* 关联 ProcessInstance 的 id 属性
|
||||
*/
|
||||
private String processInstanceId;
|
||||
/**
|
||||
* 流程实例的流程定义编号
|
||||
*
|
||||
* 关联 ProcessInstance 的 processDefinitionId 属性
|
||||
*/
|
||||
private String processDefinitionId;
|
||||
/**
|
||||
* 流程分类
|
||||
*
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,59 @@
|
||||
package cn.iocoder.yudao.module.bpm.dal.redis;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
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);
|
||||
if (StrUtil.isEmpty(infix)) {
|
||||
// 特殊:没有前缀,则不能过期,不能每次都是从 0 开始
|
||||
stringRedisTemplate.expire(key, Duration.ofDays(1L));
|
||||
}
|
||||
return noPrefix + String.format("%0" + processIdRule.getLength() + "d", no);
|
||||
}
|
||||
|
||||
}
|
@@ -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:";
|
||||
|
||||
}
|
@@ -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,
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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";
|
||||
|
||||
}
|
||||
|
@@ -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/Activiti之SkipExpression 完成自动审批</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";
|
||||
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import jakarta.annotation.Resource;
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
|
||||
}
|
@@ -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.*;
|
||||
|
||||
@@ -63,6 +64,13 @@ public class BpmnModelUtils {
|
||||
addExtensionElement(element, name, String.valueOf(value));
|
||||
}
|
||||
|
||||
public static void addExtensionElementJson(FlowElement element, String name, Object value) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
addExtensionElement(element, name, JsonUtils.toJsonString(value));
|
||||
}
|
||||
|
||||
public static void addExtensionElement(FlowElement element, String name, Map<String, String> attributes) {
|
||||
if (attributes == null) {
|
||||
return;
|
||||
@@ -170,9 +178,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 +354,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 +841,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;
|
||||
}
|
||||
}
|
||||
|
@@ -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,11 +24,11 @@ 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;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
/**
|
||||
* Flowable 相关的工具方法
|
||||
@@ -67,6 +73,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 +196,54 @@ public class FlowableUtils {
|
||||
BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程实例的摘要
|
||||
*
|
||||
* 仅有 {@link BpmModelFormTypeEnum#getType()} 表单,才有摘要。
|
||||
* 原因是,只有它才有表单项的配置,从而可以根据配置,展示摘要。
|
||||
*
|
||||
* @param processDefinitionInfo 流程定义
|
||||
* @param processVariables 流程实例的 variables
|
||||
* @return 摘要
|
||||
*/
|
||||
public static List<KeyValue<String, String>> getSummary(BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||
Map<String, Object> processVariables) {
|
||||
// 只有流程表单才会显示摘要!
|
||||
if (ObjectUtil.isNull(processDefinitionInfo)
|
||||
|| !BpmModelFormTypeEnum.NORMAL.getType().equals(processDefinitionInfo.getFormType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 解析表单配置
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
// 情况一:当自定义了摘要
|
||||
if (ObjectUtil.isNotNull(processDefinitionInfo.getSummarySetting())
|
||||
&& Boolean.TRUE.equals(processDefinitionInfo.getSummarySetting().getEnable())) {
|
||||
return convertList(processDefinitionInfo.getSummarySetting().getSummary(), item -> {
|
||||
BpmFormFieldVO formField = formFieldsMap.get(item);
|
||||
if (formField != null) {
|
||||
return new KeyValue<String, String>(formField.getTitle(),
|
||||
processVariables.getOrDefault(item, "").toString());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
// 情况二:默认摘要展示前三个表单字段
|
||||
return formFieldsMap.entrySet().stream()
|
||||
.limit(3)
|
||||
.map(entry -> new KeyValue<>(entry.getValue().getTitle(),
|
||||
processVariables.getOrDefault(entry.getValue().getField(), "").toString()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// ========== Task 相关的工具方法 ==========
|
||||
|
||||
/**
|
||||
@@ -201,6 +266,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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得任务的表单
|
||||
*
|
||||
@@ -231,9 +306,9 @@ public class FlowableUtils {
|
||||
|
||||
private static Object getExpressionValue(VariableContainer variableContainer, String expressionString,
|
||||
ProcessEngineConfigurationImpl processEngineConfiguration) {
|
||||
assert processEngineConfiguration!= null;
|
||||
assert processEngineConfiguration != null;
|
||||
ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
|
||||
assert expressionManager!= null;
|
||||
assert expressionManager != null;
|
||||
Expression expression = expressionManager.createExpression(expressionString);
|
||||
return expression.getValue(variableContainer);
|
||||
}
|
||||
|
@@ -11,15 +11,19 @@ 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 +36,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 +88,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 +103,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 +113,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 +126,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 +136,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 +154,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 +163,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 +176,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. 遍历分支节点
|
||||
// 下面的注释,以如下情况举例子。分支 1:A->B->C->D->E,分支 2:A->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 {
|
||||
// 下面的注释,以如下情况举例子。分支 1:A->B->C->D->E,分支 2:A->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 +273,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 +289,7 @@ public class SimpleModelUtils {
|
||||
throw new UnsupportedOperationException("请实现该方法");
|
||||
}
|
||||
|
||||
BpmSimpleModelNodeType getType();
|
||||
BpmSimpleModelNodeTypeEnum getType();
|
||||
|
||||
}
|
||||
|
||||
@@ -284,8 +304,8 @@ public class SimpleModelUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmSimpleModelNodeType getType() {
|
||||
return BpmSimpleModelNodeType.START_NODE;
|
||||
public BpmSimpleModelNodeTypeEnum getType() {
|
||||
return BpmSimpleModelNodeTypeEnum.START_NODE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -302,8 +322,8 @@ public class SimpleModelUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmSimpleModelNodeType getType() {
|
||||
return BpmSimpleModelNodeType.END_NODE;
|
||||
public BpmSimpleModelNodeTypeEnum getType() {
|
||||
return BpmSimpleModelNodeTypeEnum.END_NODE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -331,8 +351,8 @@ public class SimpleModelUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmSimpleModelNodeType getType() {
|
||||
return BpmSimpleModelNodeType.START_USER_NODE;
|
||||
public BpmSimpleModelNodeTypeEnum getType() {
|
||||
return BpmSimpleModelNodeTypeEnum.START_USER_NODE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -355,8 +375,8 @@ public class SimpleModelUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmSimpleModelNodeType getType() {
|
||||
return BpmSimpleModelNodeType.APPROVE_NODE;
|
||||
public BpmSimpleModelNodeTypeEnum getType() {
|
||||
return BpmSimpleModelNodeTypeEnum.APPROVE_NODE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -384,7 +404,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 +439,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 +532,8 @@ public class SimpleModelUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmSimpleModelNodeType getType() {
|
||||
return BpmSimpleModelNodeType.COPY_NODE;
|
||||
public BpmSimpleModelNodeTypeEnum getType() {
|
||||
return BpmSimpleModelNodeTypeEnum.COPY_NODE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -488,15 +548,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 +577,8 @@ public class SimpleModelUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmSimpleModelNodeType getType() {
|
||||
return BpmSimpleModelNodeType.PARALLEL_BRANCH_NODE;
|
||||
public BpmSimpleModelNodeTypeEnum getType() {
|
||||
return BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -531,7 +591,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 @jason:setName
|
||||
@@ -544,8 +604,8 @@ public class SimpleModelUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmSimpleModelNodeType getType() {
|
||||
return BpmSimpleModelNodeType.INCLUSIVE_BRANCH_NODE;
|
||||
public BpmSimpleModelNodeTypeEnum getType() {
|
||||
return BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -559,48 +619,145 @@ 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,
|
||||
BpmSimpleModelNodeVO node) {
|
||||
String conditionExpression = buildConditionExpression(node);
|
||||
String conditionExpression = buildConditionExpression(node.getConditionSetting());
|
||||
return buildBpmnSequenceFlow(sourceId, targetId, node.getId(), node.getName(), conditionExpression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造条件表达式
|
||||
*
|
||||
* @param node 条件节点
|
||||
*/
|
||||
public static String buildConditionExpression(BpmSimpleModelNodeVO node) {
|
||||
BpmSimpleModeConditionType conditionTypeEnum = BpmSimpleModeConditionType.valueOf(node.getConditionType());
|
||||
if (conditionTypeEnum == BpmSimpleModeConditionType.EXPRESSION) {
|
||||
return node.getConditionExpression();
|
||||
/**
|
||||
* 构造条件表达式
|
||||
*/
|
||||
public static String buildConditionExpression(BpmSimpleModelNodeVO.ConditionSetting conditionSetting) {
|
||||
return buildConditionExpression(conditionSetting.getConditionType(), conditionSetting.getConditionExpression(),
|
||||
conditionSetting.getConditionGroups());
|
||||
}
|
||||
|
||||
public static String buildConditionExpression(BpmSimpleModelNodeVO.RouterSetting routerSetting) {
|
||||
return buildConditionExpression(routerSetting.getConditionType(), routerSetting.getConditionExpression(),
|
||||
routerSetting.getConditionGroups());
|
||||
}
|
||||
|
||||
public static String buildConditionExpression(Integer conditionType, String conditionExpression, ConditionGroups conditionGroups) {
|
||||
BpmSimpleModeConditionTypeEnum conditionTypeEnum = BpmSimpleModeConditionTypeEnum.valueOf(conditionType);
|
||||
if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.EXPRESSION) {
|
||||
return conditionExpression;
|
||||
}
|
||||
if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.RULE) {
|
||||
if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) {
|
||||
return null;
|
||||
}
|
||||
if (conditionTypeEnum == BpmSimpleModeConditionType.RULE) {
|
||||
ConditionGroups conditionGroups = node.getConditionGroups();
|
||||
if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) {
|
||||
return null;
|
||||
List<String> strConditionGroups = CollectionUtils.convertList(conditionGroups.getConditions(), item -> {
|
||||
if (CollUtil.isEmpty(item.getRules())) {
|
||||
return "";
|
||||
}
|
||||
List<String> strConditionGroups = CollectionUtils.convertList(conditionGroups.getConditions(), item -> {
|
||||
if (CollUtil.isEmpty(item.getRules())) {
|
||||
return "";
|
||||
}
|
||||
// 构造规则表达式
|
||||
List<String> list = CollectionUtils.convertList(item.getRules(), (rule) -> {
|
||||
String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide()
|
||||
: "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号
|
||||
return String.format(" %s %s var:convertByType(%s,%s)", rule.getLeftSide(), rule.getOpCode(), rule.getLeftSide(), rightSide);
|
||||
});
|
||||
// 构造条件组的表达式
|
||||
Boolean and = item.getAnd();
|
||||
return "(" + CollUtil.join(list, and ? " && " : " || ") + ")";
|
||||
// 构造规则表达式
|
||||
List<String> list = CollectionUtils.convertList(item.getRules(), (rule) -> {
|
||||
String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide()
|
||||
: "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号
|
||||
return String.format(" %s %s var:convertByType(%s,%s)", rule.getLeftSide(), rule.getOpCode(), rule.getLeftSide(), rightSide);
|
||||
});
|
||||
return String.format("${%s}", CollUtil.join(strConditionGroups, conditionGroups.getAnd() ? " && " : " || "));
|
||||
// 构造条件组的表达式
|
||||
Boolean and = item.getAnd();
|
||||
return "(" + CollUtil.join(list, and ? " && " : " || ") + ")";
|
||||
});
|
||||
return String.format("${%s}", CollUtil.join(strConditionGroups, conditionGroups.getAnd() ? " && " : " || "));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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 null;
|
||||
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) {
|
||||
addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getHttpRequestSetting());
|
||||
}
|
||||
if (node.getTriggerSetting().getNormalFormSetting() != null) {
|
||||
addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getNormalFormSetting());
|
||||
}
|
||||
}
|
||||
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 = SimpleModelUtils.buildConditionExpression(router);
|
||||
return buildBpmnSequenceFlow(nodeId, router.getNodeId(), null, null, conditionExpression);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -620,33 +777,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.getConditionSetting()));
|
||||
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 +811,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())
|
||||
&& evalConditionExpress(variables, conditionNode));
|
||||
conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())
|
||||
&& evalConditionExpress(variables, conditionNode.getConditionSetting()));
|
||||
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 +827,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));
|
||||
@@ -680,8 +837,31 @@ public class SimpleModelUtils {
|
||||
simulateNextNode(currentNode.getChildNode(), variables, resultNodes);
|
||||
}
|
||||
|
||||
public static boolean evalConditionExpress(Map<String, Object> variables, BpmSimpleModelNodeVO conditionNode) {
|
||||
return BpmnModelUtils.evalConditionExpress(variables, ConditionNodeConvert.buildConditionExpression(conditionNode));
|
||||
public static boolean evalConditionExpress(Map<String, Object> variables, BpmSimpleModelNodeVO.ConditionSetting conditionSetting) {
|
||||
return BpmnModelUtils.evalConditionExpress(variables, buildConditionExpression(conditionSetting));
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -88,6 +88,14 @@ public interface BpmModelService {
|
||||
*/
|
||||
void deleteModel(Long userId, String id);
|
||||
|
||||
/**
|
||||
* 清理模型,包括流程实例
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param id 编号
|
||||
*/
|
||||
void cleanModel(Long userId, String id);
|
||||
|
||||
/**
|
||||
* 修改模型的状态,实际更新的部署的流程定义的状态
|
||||
*
|
||||
|
@@ -14,10 +14,12 @@ 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;
|
||||
@@ -25,10 +27,16 @@ 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;
|
||||
@@ -63,12 +71,22 @@ 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();
|
||||
if (StrUtil.isNotEmpty(name)) {
|
||||
modelQuery.modelNameLike("%" + name + "%");
|
||||
}
|
||||
modelQuery.modelTenantId(FlowableUtils.getTenantId());
|
||||
return modelQuery.list();
|
||||
}
|
||||
|
||||
@@ -246,6 +264,34 @@ 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();
|
||||
tasks.forEach(task -> taskService.deleteTask(task.getId(),BpmReasonEnum.CANCEL_BY_PROCESS_CLEAN.getReason()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateModelState(Long userId, String id, Integer state) {
|
||||
// 1.1 校验流程模型存在
|
||||
|
@@ -7,8 +7,8 @@ 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;
|
||||
@@ -52,14 +52,14 @@ 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())
|
||||
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())
|
||||
} else if (createReqVO.getType().equals(BpmProcessListenerTypeEnum.TASK.getType())
|
||||
&& !TaskListener.class.isAssignableFrom(clazz)) {
|
||||
throw exception(PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR, createReqVO.getValue(),
|
||||
TaskListener.class.getName());
|
||||
|
@@ -50,4 +50,11 @@ public interface BpmProcessInstanceCopyService {
|
||||
PageResult<BpmProcessInstanceCopyDO> getProcessInstanceCopyPage(Long userId,
|
||||
BpmProcessInstanceCopyPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 删除抄送流程
|
||||
*
|
||||
* @param processInstanceId 流程实例 ID
|
||||
*/
|
||||
void deleteProcessInstanceCopy(String processInstanceId);
|
||||
|
||||
}
|
||||
|
@@ -77,7 +77,8 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
|
||||
.setUserId(userId).setReason(reason).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
|
||||
.setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
|
||||
.setCategory(processDefinition.getCategory()).setTaskId(taskId)
|
||||
.setActivityId(activityId).setActivityName(activityName));
|
||||
.setActivityId(activityId).setActivityName(activityName)
|
||||
.setProcessDefinitionId(processInstance.getProcessDefinitionId()));
|
||||
processInstanceCopyMapper.insertBatch(copyList);
|
||||
}
|
||||
|
||||
@@ -87,4 +88,9 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
|
||||
return processInstanceCopyMapper.selectPage(userId, pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProcessInstanceCopy(String processInstanceId) {
|
||||
processInstanceCopyMapper.deleteByProcessInstanceId(processInstanceId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -157,4 +157,12 @@ public interface BpmProcessInstanceService {
|
||||
*/
|
||||
void processProcessInstanceCompleted(ProcessInstance instance);
|
||||
|
||||
/**
|
||||
* 更新 ProcessInstance 的变量
|
||||
*
|
||||
* @param id 流程编号
|
||||
* @param variables 流程变量
|
||||
*/
|
||||
void updateProcessInstanceVariables(String id, Map<String, Object> variables);
|
||||
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.service.task;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
@@ -12,15 +13,17 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
|
||||
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNodeTask;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
|
||||
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.redis.BpmProcessIdRedisDAO;
|
||||
import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeTypeEnum;
|
||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
|
||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
|
||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
|
||||
@@ -50,6 +53,7 @@ import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.history.HistoricProcessInstanceQuery;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.engine.runtime.ProcessInstanceBuilder;
|
||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -109,6 +113,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
@Resource
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
@Resource
|
||||
private BpmProcessIdRedisDAO processIdRedisDAO;
|
||||
|
||||
// ========== Query 查询相关方法 ==========
|
||||
|
||||
@Override
|
||||
@@ -121,61 +128,23 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
|
||||
@Override
|
||||
public List<ProcessInstance> getProcessInstances(Set<String> ids) {
|
||||
return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
|
||||
return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).includeProcessVariables().list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HistoricProcessInstance getHistoricProcessInstance(String id) {
|
||||
return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult();
|
||||
return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables()
|
||||
.singleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) {
|
||||
return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
|
||||
return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).includeProcessVariables()
|
||||
.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId,
|
||||
BpmProcessInstancePageReqVO pageReqVO) {
|
||||
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
|
||||
HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery()
|
||||
.includeProcessVariables()
|
||||
.processInstanceTenantId(FlowableUtils.getTenantId())
|
||||
.orderByProcessInstanceStartTime().desc();
|
||||
if (userId != null) { // 【我的流程】菜单时,需要传递该字段
|
||||
processInstanceQuery.startedBy(String.valueOf(userId));
|
||||
} else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段
|
||||
processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId()));
|
||||
}
|
||||
if (StrUtil.isNotEmpty(pageReqVO.getName())) {
|
||||
processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%");
|
||||
}
|
||||
if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionKey())) {
|
||||
processInstanceQuery.processDefinitionKey(pageReqVO.getProcessDefinitionKey());
|
||||
}
|
||||
if (StrUtil.isNotEmpty(pageReqVO.getCategory())) {
|
||||
processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory());
|
||||
}
|
||||
if (pageReqVO.getStatus() != null) {
|
||||
processInstanceQuery.variableValueEquals(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus());
|
||||
}
|
||||
if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) {
|
||||
processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0]));
|
||||
processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1]));
|
||||
}
|
||||
// 查询数量
|
||||
long processInstanceCount = processInstanceQuery.count();
|
||||
if (processInstanceCount == 0) {
|
||||
return PageResult.empty(processInstanceCount);
|
||||
}
|
||||
// 查询列表
|
||||
List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize());
|
||||
return new PageResult<>(processInstanceList, processInstanceCount);
|
||||
}
|
||||
|
||||
|
||||
private Map<String, String> getFormFieldsPermission(BpmnModel bpmnModel,
|
||||
String activityId, String taskId) {
|
||||
String activityId, String taskId) {
|
||||
// 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id
|
||||
if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) {
|
||||
activityId = Optional.ofNullable(taskService.getHistoricTask(taskId))
|
||||
@@ -208,8 +177,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
}
|
||||
// 1.3 读取其它相关数据
|
||||
ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(
|
||||
historicProcessInstance != null ? historicProcessInstance.getProcessDefinitionId() : reqVO.getProcessDefinitionId());
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinition.getId());
|
||||
historicProcessInstance != null ? historicProcessInstance.getProcessDefinitionId()
|
||||
: reqVO.getProcessDefinitionId());
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService
|
||||
.getProcessDefinitionInfo(processDefinition.getId());
|
||||
BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());
|
||||
|
||||
// 2.1 已结束 + 进行中的活动节点
|
||||
@@ -218,24 +189,29 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
List<HistoricActivityInstance> activities = null; // 流程实例列表
|
||||
if (reqVO.getProcessInstanceId() != null) {
|
||||
activities = taskService.getActivityListByProcessInstanceId(reqVO.getProcessInstanceId());
|
||||
List<HistoricTaskInstance> tasks = taskService.getTaskListByProcessInstanceId(reqVO.getProcessInstanceId(), true);
|
||||
List<HistoricTaskInstance> tasks = taskService.getTaskListByProcessInstanceId(reqVO.getProcessInstanceId(),
|
||||
true);
|
||||
endActivityNodes = getEndActivityNodeList(startUserId, bpmnModel, processDefinitionInfo,
|
||||
historicProcessInstance, processInstanceStatus, activities, tasks);
|
||||
runActivityNodes = getRunApproveNodeList(startUserId, bpmnModel, processDefinition, processVariables, activities, tasks);
|
||||
runActivityNodes = getRunApproveNodeList(startUserId, bpmnModel, processDefinition, processVariables,
|
||||
activities, tasks);
|
||||
}
|
||||
|
||||
// 2.2 流程已经结束,直接 return,无需预测
|
||||
if (BpmProcessInstanceStatusEnum.isProcessEndStatus(processInstanceStatus)) {
|
||||
return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance,
|
||||
return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo,
|
||||
historicProcessInstance,
|
||||
processInstanceStatus, endActivityNodes, runActivityNodes, null, null);
|
||||
}
|
||||
|
||||
// 3.1 计算当前登录用户的待办任务
|
||||
// TODO @jason:有一个极端情况,如果一个用户有 2 个 task A 和 B,A 已经通过,B 需要审核。这个时,通过 A 进来,todo 拿到 B,会不会表单权限不一致哈。
|
||||
// TODO @jason:有一个极端情况,如果一个用户有 2 个 task A 和 B,A 已经通过,B 需要审核。这个时,通过 A 进来,todo 拿到
|
||||
// B,会不会表单权限不一致哈。
|
||||
BpmTaskRespVO todoTask = taskService.getFirstTodoTask(loginUserId, reqVO.getProcessInstanceId());
|
||||
|
||||
// 3.2 预测未运行节点的审批信息
|
||||
List<ActivityNode> simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo,
|
||||
List<ActivityNode> simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel,
|
||||
processDefinitionInfo,
|
||||
processVariables, activities);
|
||||
|
||||
// 4. 拼接最终数据
|
||||
@@ -243,34 +219,93 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
processInstanceStatus, endActivityNodes, runActivityNodes, simulateActivityNodes, todoTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId,
|
||||
BpmProcessInstancePageReqVO pageReqVO) {
|
||||
// 1. 构建查询条件
|
||||
HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery()
|
||||
.includeProcessVariables()
|
||||
.processInstanceTenantId(FlowableUtils.getTenantId())
|
||||
.orderByProcessInstanceStartTime().desc();
|
||||
if (userId != null) { // 【我的流程】菜单时,需要传递该字段
|
||||
processInstanceQuery.startedBy(String.valueOf(userId));
|
||||
} else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段
|
||||
processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId()));
|
||||
}
|
||||
if (StrUtil.isNotEmpty(pageReqVO.getName())) {
|
||||
processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%");
|
||||
}
|
||||
if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionKey())) {
|
||||
processInstanceQuery.processDefinitionKey(pageReqVO.getProcessDefinitionKey());
|
||||
}
|
||||
if (StrUtil.isNotEmpty(pageReqVO.getCategory())) {
|
||||
processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory());
|
||||
}
|
||||
if (pageReqVO.getStatus() != null) {
|
||||
processInstanceQuery.variableValueEquals(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
|
||||
pageReqVO.getStatus());
|
||||
}
|
||||
if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) {
|
||||
processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0]));
|
||||
processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1]));
|
||||
}
|
||||
if (ArrayUtil.isNotEmpty(pageReqVO.getEndTime())) {
|
||||
processInstanceQuery.finishedAfter(DateUtils.of(pageReqVO.getEndTime()[0]));
|
||||
processInstanceQuery.finishedBefore(DateUtils.of(pageReqVO.getEndTime()[1]));
|
||||
}
|
||||
// 表单字段查询
|
||||
Map<String, Object> formFieldsParams = JsonUtils.parseObject(pageReqVO.getFormFieldsParams(), Map.class);
|
||||
if (CollUtil.isNotEmpty(formFieldsParams)) {
|
||||
formFieldsParams.forEach((key, value) -> {
|
||||
if (StrUtil.isEmpty(String.valueOf(value))) {
|
||||
return;
|
||||
}
|
||||
// TODO @lesan:应支持多种类型的查询方式,目前只有字符串全等
|
||||
processInstanceQuery.variableValueEquals(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
// 2.1 查询数量
|
||||
long processInstanceCount = processInstanceQuery.count();
|
||||
if (processInstanceCount == 0) {
|
||||
return PageResult.empty(processInstanceCount);
|
||||
}
|
||||
// 2.2 查询列表
|
||||
List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO),
|
||||
pageReqVO.getPageSize());
|
||||
return new PageResult<>(processInstanceList, processInstanceCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接审批详情的最终数据
|
||||
* <p>
|
||||
* 主要是,拼接审批人的用户信息、部门信息
|
||||
*/
|
||||
private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO,
|
||||
BpmnModel bpmnModel,
|
||||
ProcessDefinition processDefinition,
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||
HistoricProcessInstance processInstance,
|
||||
Integer processInstanceStatus,
|
||||
List<ActivityNode> endApprovalNodeInfos,
|
||||
List<ActivityNode> runningApprovalNodeInfos,
|
||||
List<ActivityNode> simulateApprovalNodeInfos,
|
||||
BpmTaskRespVO todoTask) {
|
||||
BpmnModel bpmnModel,
|
||||
ProcessDefinition processDefinition,
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||
HistoricProcessInstance processInstance,
|
||||
Integer processInstanceStatus,
|
||||
List<ActivityNode> endApprovalNodeInfos,
|
||||
List<ActivityNode> runningApprovalNodeInfos,
|
||||
List<ActivityNode> simulateApprovalNodeInfos,
|
||||
BpmTaskRespVO todoTask) {
|
||||
// 1. 获取所有需要读取用户信息的 userIds
|
||||
List<ActivityNode> approveNodes = newArrayList(asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos));
|
||||
List<ActivityNode> approveNodes = newArrayList(
|
||||
asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos));
|
||||
Set<Long> userIds = BpmProcessInstanceConvert.INSTANCE.parseUserIds(processInstance, approveNodes, todoTask);
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
|
||||
|
||||
// 2. 表单权限
|
||||
String taskId = reqVO.getTaskId() == null && todoTask != null ? todoTask.getId() : reqVO.getTaskId();
|
||||
Map<String, String> formFieldsPermission = getFormFieldsPermission(bpmnModel, reqVO.getActivityId(), taskId);
|
||||
|
||||
// 3. 拼接数据
|
||||
return BpmProcessInstanceConvert.INSTANCE.buildApprovalDetail(bpmnModel, processDefinition, processDefinitionInfo, processInstance,
|
||||
return BpmProcessInstanceConvert.INSTANCE.buildApprovalDetail(bpmnModel, processDefinition,
|
||||
processDefinitionInfo, processInstance,
|
||||
processInstanceStatus, approveNodes, todoTask, formFieldsPermission, userMap, deptMap);
|
||||
}
|
||||
|
||||
@@ -278,17 +313,19 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
* 获得【已结束】的活动节点们
|
||||
*/
|
||||
private List<ActivityNode> getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel,
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||
HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus,
|
||||
List<HistoricActivityInstance> activities, List<HistoricTaskInstance> tasks) {
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||
HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus,
|
||||
List<HistoricActivityInstance> activities, List<HistoricTaskInstance> tasks) {
|
||||
// 遍历 tasks 列表,只处理已结束的 UserTask
|
||||
// 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities 的话,它无法成为一个节点
|
||||
// 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities
|
||||
// 的话,它无法成为一个节点
|
||||
List<HistoricTaskInstance> endTasks = filterList(tasks, task -> task.getEndTime() != null);
|
||||
List<ActivityNode> approvalNodes = convertList(endTasks, task -> {
|
||||
FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
|
||||
ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName())
|
||||
.setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) ?
|
||||
BpmSimpleModelNodeType.START_USER_NODE.getType() : BpmSimpleModelNodeType.APPROVE_NODE.getType())
|
||||
.setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey())
|
||||
? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType()
|
||||
: BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
|
||||
.setStatus(FlowableUtils.getTaskStatus(task))
|
||||
.setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode))
|
||||
.setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime()))
|
||||
@@ -310,10 +347,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
ActivityNodeTask startTask = new ActivityNodeTask().setId(BpmnModelConstants.START_USER_NODE_ID)
|
||||
.setAssignee(startUserId).setStatus(BpmTaskStatusEnum.APPROVE.getStatus());
|
||||
ActivityNode startNode = new ActivityNode().setId(startTask.getId())
|
||||
.setName(BpmSimpleModelNodeType.START_USER_NODE.getName())
|
||||
.setNodeType(BpmSimpleModelNodeType.START_USER_NODE.getType())
|
||||
.setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName())
|
||||
.setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType())
|
||||
.setStatus(startTask.getStatus()).setTasks(ListUtil.of(startTask))
|
||||
.setStartTime(DateUtils.of(activity.getStartTime())).setEndTime(DateUtils.of(activity.getEndTime()));
|
||||
.setStartTime(DateUtils.of(activity.getStartTime()))
|
||||
.setEndTime(DateUtils.of(activity.getEndTime()));
|
||||
approvalNodes.add(0, startNode);
|
||||
return;
|
||||
}
|
||||
@@ -324,9 +362,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
return;
|
||||
}
|
||||
ActivityNode endNode = new ActivityNode().setId(activity.getId())
|
||||
.setName(BpmSimpleModelNodeType.END_NODE.getName())
|
||||
.setNodeType(BpmSimpleModelNodeType.END_NODE.getType()).setStatus(processInstanceStatus)
|
||||
.setStartTime(DateUtils.of(activity.getStartTime())).setEndTime(DateUtils.of(activity.getEndTime()));
|
||||
.setName(BpmSimpleModelNodeTypeEnum.END_NODE.getName())
|
||||
.setNodeType(BpmSimpleModelNodeTypeEnum.END_NODE.getType()).setStatus(processInstanceStatus)
|
||||
.setStartTime(DateUtils.of(activity.getStartTime()))
|
||||
.setEndTime(DateUtils.of(activity.getEndTime()));
|
||||
String reason = FlowableUtils.getProcessInstanceReason(historicProcessInstance);
|
||||
if (StrUtil.isNotEmpty(reason)) {
|
||||
endNode.setTasks(singletonList(new ActivityNodeTask().setId(endNode.getId())
|
||||
@@ -342,15 +381,16 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
* 获得【进行中】的活动节点们
|
||||
*/
|
||||
private List<ActivityNode> getRunApproveNodeList(Long startUserId,
|
||||
BpmnModel bpmnModel,
|
||||
ProcessDefinition processDefinition,
|
||||
Map<String, Object> processVariables,
|
||||
List<HistoricActivityInstance> activities,
|
||||
List<HistoricTaskInstance> tasks) {
|
||||
BpmnModel bpmnModel,
|
||||
ProcessDefinition processDefinition,
|
||||
Map<String, Object> processVariables,
|
||||
List<HistoricActivityInstance> activities,
|
||||
List<HistoricTaskInstance> tasks) {
|
||||
// 构建运行中的任务,基于 activityId 分组
|
||||
List<HistoricActivityInstance> runActivities = filterList(activities, activity -> activity.getEndTime() == null
|
||||
&& (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER)));
|
||||
Map<String, List<HistoricActivityInstance>> runningTaskMap = convertMultiMap(runActivities, HistoricActivityInstance::getActivityId);
|
||||
Map<String, List<HistoricActivityInstance>> runningTaskMap = convertMultiMap(runActivities,
|
||||
HistoricActivityInstance::getActivityId);
|
||||
|
||||
// 按照 activityId 分组,构建 ApprovalNodeInfo 节点
|
||||
Map<String, HistoricTaskInstance> taskMap = convertMap(tasks, HistoricTaskInstance::getId);
|
||||
@@ -360,8 +400,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
// 构建活动节点
|
||||
FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, activityId);
|
||||
HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同
|
||||
ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()).setName(firstActivity.getActivityName())
|
||||
.setNodeType(BpmSimpleModelNodeType.APPROVE_NODE.getType()).setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
|
||||
ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId())
|
||||
.setName(firstActivity.getActivityName())
|
||||
.setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
|
||||
.setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
|
||||
.setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode))
|
||||
.setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime()))
|
||||
.setTasks(new ArrayList<>());
|
||||
@@ -374,7 +416,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
taskService.getAllChildrenTaskListByParentTaskId(activity.getTaskId(), tasks),
|
||||
childTask -> childTask.getEndTime() == null);
|
||||
if (CollUtil.isNotEmpty(childrenTasks)) {
|
||||
activityNode.getTasks().addAll(convertList(childrenTasks, BpmProcessInstanceConvert.INSTANCE::buildApprovalTaskInfo));
|
||||
activityNode.getTasks().addAll(
|
||||
convertList(childrenTasks, BpmProcessInstanceConvert.INSTANCE::buildApprovalTaskInfo));
|
||||
}
|
||||
}
|
||||
// 处理每个任务的 candidateUsers 属性:如果是依次审批,需要预测它的后续审批人。因为 Task 是审批完一个,创建一个新的 Task
|
||||
@@ -384,8 +427,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
// 截取当前审批人位置后面的候选人,不包含当前审批人
|
||||
ActivityNodeTask approvalTaskInfo = CollUtil.getFirst(activityNode.getTasks());
|
||||
Assert.notNull(approvalTaskInfo, "任务不能为空");
|
||||
int index = CollUtil.indexOf(candidateUserIds, userId -> ObjectUtils.equalsAny(userId, approvalTaskInfo.getOwner(),
|
||||
approvalTaskInfo.getAssignee())); // 委派或者向前加签情况,需要先比较 owner
|
||||
int index = CollUtil.indexOf(candidateUserIds,
|
||||
userId -> ObjectUtils.equalsAny(userId, approvalTaskInfo.getOwner(),
|
||||
approvalTaskInfo.getAssignee())); // 委派或者向前加签情况,需要先比较 owner
|
||||
activityNode.setCandidateUserIds(CollUtil.sub(candidateUserIds, index + 1, candidateUserIds.size()));
|
||||
}
|
||||
return activityNode;
|
||||
@@ -396,10 +440,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
* 获得【预测(未来)】的活动节点们
|
||||
*/
|
||||
private List<ActivityNode> getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel,
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||
Map<String, Object> processVariables,
|
||||
List<HistoricActivityInstance> activities) {
|
||||
// TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance 包括了历史的操作,不是只有 startEvent 到当前节点的记录
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||
Map<String, Object> processVariables,
|
||||
List<HistoricActivityInstance> activities) {
|
||||
// TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance
|
||||
// 包括了历史的操作,不是只有 startEvent 到当前节点的记录
|
||||
Set<String> runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId);
|
||||
// 情况一:BPMN 设计器
|
||||
if (Objects.equals(BpmModelTypeEnum.BPMN.getType(), processDefinitionInfo.getModelType())) {
|
||||
@@ -409,7 +454,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
}
|
||||
// 情况二:SIMPLE 设计器
|
||||
if (Objects.equals(BpmModelTypeEnum.SIMPLE.getType(), processDefinitionInfo.getModelType())) {
|
||||
BpmSimpleModelNodeVO simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(), BpmSimpleModelNodeVO.class);
|
||||
BpmSimpleModelNodeVO simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(),
|
||||
BpmSimpleModelNodeVO.class);
|
||||
List<BpmSimpleModelNodeVO> simpleNodes = SimpleModelUtils.simulateProcess(simpleModel, processVariables);
|
||||
return convertList(simpleNodes, simpleNode -> buildNotRunApproveNodeForSimple(startUserId, bpmnModel,
|
||||
processDefinitionInfo, processVariables, simpleNode, runActivityIds));
|
||||
@@ -418,9 +464,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
}
|
||||
|
||||
private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel,
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
|
||||
BpmSimpleModelNodeVO node, Set<String> runActivityIds) {
|
||||
// TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance 包括了历史的操作,不是只有 startEvent 到当前节点的记录
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
|
||||
BpmSimpleModelNodeVO node, Set<String> runActivityIds) {
|
||||
// TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance
|
||||
// 包括了历史的操作,不是只有 startEvent 到当前节点的记录
|
||||
if (runActivityIds.contains(node.getId())) {
|
||||
return null;
|
||||
}
|
||||
@@ -431,8 +478,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
|
||||
// 1. 开始节点/审批节点
|
||||
if (ObjectUtils.equalsAny(node.getType(),
|
||||
BpmSimpleModelNodeType.START_USER_NODE.getType(),
|
||||
BpmSimpleModelNodeType.APPROVE_NODE.getType())) {
|
||||
BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType(),
|
||||
BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) {
|
||||
List<Long> candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(),
|
||||
startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables);
|
||||
activityNode.setCandidateUserIds(candidateUserIds);
|
||||
@@ -440,51 +487,52 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
}
|
||||
|
||||
// 2. 结束节点
|
||||
if (BpmSimpleModelNodeType.END_NODE.getType().equals(node.getType())) {
|
||||
if (BpmSimpleModelNodeTypeEnum.END_NODE.getType().equals(node.getType())) {
|
||||
return activityNode;
|
||||
}
|
||||
|
||||
// 3. 抄送节点
|
||||
if (CollUtil.isEmpty(runActivityIds) && // 流程发起时:需要展示抄送节点,用于选择抄送人
|
||||
BpmSimpleModelNodeType.COPY_NODE.getType().equals(node.getType())) {
|
||||
BpmSimpleModelNodeTypeEnum.COPY_NODE.getType().equals(node.getType())) {
|
||||
return activityNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel,
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
|
||||
FlowElement node, Set<String> runActivityIds) {
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
|
||||
FlowElement node, Set<String> runActivityIds) {
|
||||
if (runActivityIds.contains(node.getId())) {
|
||||
return null;
|
||||
}
|
||||
ActivityNode activityNode = new ActivityNode().setId(node.getId()).setStatus(BpmTaskStatusEnum.NOT_START.getStatus());
|
||||
ActivityNode activityNode = new ActivityNode().setId(node.getId())
|
||||
.setStatus(BpmTaskStatusEnum.NOT_START.getStatus());
|
||||
|
||||
// 1. 开始节点
|
||||
if (node instanceof StartEvent) {
|
||||
return activityNode.setName(BpmSimpleModelNodeType.START_USER_NODE.getName())
|
||||
.setNodeType(BpmSimpleModelNodeType.START_USER_NODE.getType());
|
||||
return activityNode.setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName())
|
||||
.setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType());
|
||||
}
|
||||
|
||||
// 2. 审批节点
|
||||
if (node instanceof UserTask) {
|
||||
List<Long> candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(),
|
||||
startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables);
|
||||
return activityNode.setName(node.getName()).setNodeType(BpmSimpleModelNodeType.APPROVE_NODE.getType())
|
||||
return activityNode.setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
|
||||
.setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node))
|
||||
.setCandidateUserIds(candidateUserIds);
|
||||
}
|
||||
|
||||
// 3. 结束节点
|
||||
if (node instanceof EndEvent) {
|
||||
return activityNode.setName(BpmSimpleModelNodeType.END_NODE.getName())
|
||||
.setNodeType(BpmSimpleModelNodeType.END_NODE.getType());
|
||||
return activityNode.setName(BpmSimpleModelNodeTypeEnum.END_NODE.getName())
|
||||
.setNodeType(BpmSimpleModelNodeTypeEnum.END_NODE.getType());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Long> getTaskCandidateUserList(BpmnModel bpmnModel, String activityId,
|
||||
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
|
||||
Set<Long> userIds = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId,
|
||||
startUserId, processDefinitionId, processVariables);
|
||||
return new ArrayList<>(userIds);
|
||||
@@ -498,14 +546,16 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
return null;
|
||||
}
|
||||
// 1.2 获得流程定义
|
||||
BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId());
|
||||
BpmnModel bpmnModel = processDefinitionService
|
||||
.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId());
|
||||
if (bpmnModel == null) {
|
||||
return null;
|
||||
}
|
||||
BpmSimpleModelNodeVO simpleModel = null;
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(
|
||||
processInstance.getProcessDefinitionId());
|
||||
if (processDefinitionInfo != null && BpmModelTypeEnum.SIMPLE.getType().equals(processDefinitionInfo.getModelType())) {
|
||||
if (processDefinitionInfo != null
|
||||
&& BpmModelTypeEnum.SIMPLE.getType().equals(processDefinitionInfo.getModelType())) {
|
||||
simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(), BpmSimpleModelNodeVO.class);
|
||||
}
|
||||
// 1.3 获得流程实例对应的活动实例列表 + 任务列表
|
||||
@@ -517,10 +567,12 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
activityInstance -> activityInstance.getEndTime() == null);
|
||||
Set<String> finishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId,
|
||||
activityInstance -> activityInstance.getEndTime() != null
|
||||
&& ObjectUtil.notEqual(activityInstance.getActivityType(), BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW));
|
||||
&& ObjectUtil.notEqual(activityInstance.getActivityType(),
|
||||
BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW));
|
||||
Set<String> finishedSequenceFlowActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId,
|
||||
activityInstance -> activityInstance.getEndTime() != null
|
||||
&& ObjectUtil.equals(activityInstance.getActivityType(), BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW));
|
||||
&& ObjectUtil.equals(activityInstance.getActivityType(),
|
||||
BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW));
|
||||
// 特殊:会签情况下,会有部分已完成(审批)、部分未完成(待审批),此时需要 finishedTaskActivityIds 移除掉
|
||||
finishedTaskActivityIds.removeAll(unfinishedTaskActivityIds);
|
||||
// 特殊:如果流程实例被拒绝,则需要计算是哪个活动节点。
|
||||
@@ -538,8 +590,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
Set<Long> userIds = BpmProcessInstanceConvert.INSTANCE.parseUserIds02(processInstance, tasks);
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
return BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceBpmnModelView(processInstance, tasks, bpmnModel, simpleModel,
|
||||
unfinishedTaskActivityIds, finishedTaskActivityIds, finishedSequenceFlowActivityIds, rejectTaskActivityIds,
|
||||
return BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceBpmnModelView(processInstance, tasks, bpmnModel,
|
||||
simpleModel,
|
||||
unfinishedTaskActivityIds, finishedTaskActivityIds, finishedSequenceFlowActivityIds,
|
||||
rejectTaskActivityIds,
|
||||
userMap, deptMap);
|
||||
}
|
||||
|
||||
@@ -549,7 +603,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
|
||||
// 获得流程定义
|
||||
ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
|
||||
ProcessDefinition definition = processDefinitionService
|
||||
.getProcessDefinition(createReqVO.getProcessDefinitionId());
|
||||
// 发起流程
|
||||
return createProcessInstance0(userId, definition, createReqVO.getVariables(), null,
|
||||
createReqVO.getStartUserSelectAssignees());
|
||||
@@ -559,16 +614,18 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
|
||||
return FlowableUtils.executeAuthenticatedUserId(userId, () -> {
|
||||
// 获得流程定义
|
||||
ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
|
||||
ProcessDefinition definition = processDefinitionService
|
||||
.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
|
||||
// 发起流程
|
||||
return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(),
|
||||
return createProcessInstance0(userId, definition, createReqDTO.getVariables(),
|
||||
createReqDTO.getBusinessKey(),
|
||||
createReqDTO.getStartUserSelectAssignees());
|
||||
});
|
||||
}
|
||||
|
||||
private String createProcessInstance0(Long userId, ProcessDefinition definition,
|
||||
Map<String, Object> variables, String businessKey,
|
||||
Map<String, List<Long>> startUserSelectAssignees) {
|
||||
Map<String, Object> variables, String businessKey,
|
||||
Map<String, List<Long>> startUserSelectAssignees) {
|
||||
// 1.1 校验流程定义
|
||||
if (definition == null) {
|
||||
throw exception(PROCESS_DEFINITION_NOT_EXISTS);
|
||||
@@ -576,7 +633,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
if (definition.isSuspended()) {
|
||||
throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
|
||||
}
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(definition.getId());
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService
|
||||
.getProcessDefinitionInfo(definition.getId());
|
||||
if (processDefinitionInfo == null) {
|
||||
throw exception(PROCESS_DEFINITION_NOT_EXISTS);
|
||||
}
|
||||
@@ -595,19 +653,43 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量,发起人 ID
|
||||
variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中
|
||||
BpmProcessInstanceStatusEnum.RUNNING.getStatus());
|
||||
variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为
|
||||
// true,不影响没配置
|
||||
// skipExpression 的节点
|
||||
if (CollUtil.isNotEmpty(startUserSelectAssignees)) {
|
||||
variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees);
|
||||
variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,
|
||||
startUserSelectAssignees);
|
||||
}
|
||||
ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
|
||||
|
||||
// 3. 创建流程
|
||||
ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder()
|
||||
.processDefinitionId(definition.getId())
|
||||
.businessKey(businessKey)
|
||||
.name(definition.getName().trim())
|
||||
.variables(variables)
|
||||
.start();
|
||||
.variables(variables);
|
||||
// 3.1 创建流程 ID
|
||||
BpmModelMetaInfoVO.ProcessIdRule processIdRule = processDefinitionInfo.getProcessIdRule();
|
||||
if (processIdRule != null && Boolean.TRUE.equals(processIdRule.getEnable())) {
|
||||
processInstanceBuilder.predefineProcessInstanceId(processIdRedisDAO.generate(processIdRule));
|
||||
}
|
||||
// 3.2 流程名称
|
||||
BpmModelMetaInfoVO.TitleSetting titleSetting = processDefinitionInfo.getTitleSetting();
|
||||
if (titleSetting != null && Boolean.TRUE.equals(titleSetting.getEnable())) {
|
||||
AdminUserRespDTO user = adminUserApi.getUser(userId);
|
||||
Map<String, Object> cloneVariables = new HashMap<>(variables);
|
||||
cloneVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, user.getNickname());
|
||||
cloneVariables.put(BpmnVariableConstants.PROCESS_START_TIME, DateUtil.now());
|
||||
cloneVariables.put(BpmnVariableConstants.PROCESS_DEFINITION_NAME, definition.getName().trim());
|
||||
processInstanceBuilder.name(StrUtil.format(titleSetting.getTitle(), cloneVariables));
|
||||
} else {
|
||||
processInstanceBuilder.name(definition.getName().trim());
|
||||
}
|
||||
// 3.3 发起流程实例
|
||||
ProcessInstance instance = processInstanceBuilder.start();
|
||||
return instance.getId();
|
||||
}
|
||||
|
||||
private void validateStartUserSelectAssignees(ProcessDefinition definition, Map<String, List<Long>> startUserSelectAssignees) {
|
||||
private void validateStartUserSelectAssignees(ProcessDefinition definition,
|
||||
Map<String, List<Long>> startUserSelectAssignees) {
|
||||
// 1. 获得发起人自选审批人的 UserTask/ServiceTask 列表
|
||||
BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId());
|
||||
List<Task> tasks = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectTaskList(bpmnModel);
|
||||
@@ -641,6 +723,14 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
|
||||
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
|
||||
}
|
||||
// 1.3 校验允许撤销审批中的申请
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService
|
||||
.getProcessDefinitionInfo(instance.getProcessDefinitionId());
|
||||
Assert.notNull(processDefinitionInfo, "流程定义({})不存在", processDefinitionInfo);
|
||||
if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消
|
||||
&& Boolean.FALSE.equals(processDefinitionInfo.getAllowCancelRunningProcess())) {
|
||||
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW);
|
||||
}
|
||||
|
||||
// 2. 取消流程
|
||||
updateProcessInstanceCancel(cancelReqVO.getId(),
|
||||
@@ -668,14 +758,16 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON, reason);
|
||||
|
||||
// 2. 结束流程
|
||||
taskService.moveTaskToEnd(id);
|
||||
taskService.moveTaskToEnd(id, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProcessInstanceReject(ProcessInstance processInstance, String reason) {
|
||||
runtimeService.setVariable(processInstance.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
|
||||
runtimeService.setVariable(processInstance.getProcessInstanceId(),
|
||||
BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
|
||||
BpmProcessInstanceStatusEnum.REJECT.getStatus());
|
||||
runtimeService.setVariable(processInstance.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON,
|
||||
runtimeService.setVariable(processInstance.getProcessInstanceId(),
|
||||
BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON,
|
||||
BpmReasonEnum.REJECT_TASK.format(reason));
|
||||
}
|
||||
|
||||
@@ -686,18 +778,22 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
// 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号
|
||||
FlowableUtils.execute(instance.getTenantId(), () -> {
|
||||
// 1.1 获取当前状态
|
||||
Integer status = (Integer) instance.getProcessVariables().get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
|
||||
String reason = (String) instance.getProcessVariables().get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON);
|
||||
Integer status = (Integer) instance.getProcessVariables()
|
||||
.get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
|
||||
String reason = (String) instance.getProcessVariables()
|
||||
.get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON);
|
||||
// 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态
|
||||
// 为什么这么处理?因为流程完成,并且完成了,说明审批通过了
|
||||
if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) {
|
||||
status = BpmProcessInstanceStatusEnum.APPROVE.getStatus();
|
||||
runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, status);
|
||||
runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
|
||||
status);
|
||||
}
|
||||
|
||||
// 2. 发送对应的消息通知
|
||||
if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
|
||||
messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance));
|
||||
messageService.sendMessageWhenProcessInstanceApprove(
|
||||
BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance));
|
||||
} else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) {
|
||||
messageService.sendMessageWhenProcessInstanceReject(
|
||||
BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason));
|
||||
@@ -709,4 +805,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProcessInstanceVariables(String id, Map<String, Object> variables) {
|
||||
runtimeService.setVariables(id, variables);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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);
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
@@ -33,15 +34,13 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
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,7 @@ 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.*;
|
||||
|
||||
/**
|
||||
* 流程任务实例 Service 实现类
|
||||
@@ -160,13 +160,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 +482,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 +507,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 +656,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 +666,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 +837,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 +863,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 +1171,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 +1254,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
|
||||
messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
|
||||
// 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号
|
||||
FlowableUtils.execute(processInstance.getTenantId(), () -> {
|
||||
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
|
||||
messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1238,6 +1303,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 生效问题
|
||||
*
|
||||
|
@@ -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 芋艿:待定!
|
||||
}
|
||||
}
|
@@ -0,0 +1,124 @@
|
||||
package cn.iocoder.yudao.module.bpm.service.task.trigger;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
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 com.fasterxml.jackson.core.type.TypeReference;
|
||||
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.HashMap;
|
||||
import java.util.List;
|
||||
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);
|
||||
ResponseEntity<String> responseEntity;
|
||||
try {
|
||||
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());
|
||||
return;
|
||||
}
|
||||
|
||||
// 4.1 判断是否需要解析返回值
|
||||
if (StrUtil.isEmpty(responseEntity.getBody())
|
||||
|| !responseEntity.getStatusCode().is2xxSuccessful()
|
||||
|| CollUtil.isEmpty(setting.getResponse())) {
|
||||
return;
|
||||
}
|
||||
// 4.2 解析返回值, 返回值必须符合 CommonResult 规范。
|
||||
CommonResult<Map<String, Object>> respResult = JsonUtils.parseObjectQuietly(
|
||||
responseEntity.getBody(), new TypeReference<>() {});
|
||||
if (respResult == null || !respResult.isSuccess()){
|
||||
return;
|
||||
}
|
||||
// 4.3 获取需要更新的流程变量
|
||||
Map<String, Object> updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), setting.getResponse());
|
||||
// 4.4 更新流程变量
|
||||
if (CollUtil.isNotEmpty(updateVariables)) {
|
||||
processInstanceService.updateProcessInstanceVariables(processInstanceId, updateVariables);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求返回值获取需要更新的流程变量
|
||||
*
|
||||
* @param result 请求返回结果
|
||||
* @param responseSettings 返回设置
|
||||
* @return 需要更新的流程变量
|
||||
*/
|
||||
private Map<String, Object> getNeedUpdatedVariablesFromResponse(Map<String,Object> result,
|
||||
List<KeyValue<String, String>> responseSettings) {
|
||||
Map<String, Object> updateVariables = new HashMap<>();
|
||||
if (CollUtil.isEmpty(result)) {
|
||||
return updateVariables;
|
||||
}
|
||||
responseSettings.forEach(responseSetting -> {
|
||||
if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) {
|
||||
updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue()));
|
||||
}
|
||||
});
|
||||
return updateVariables;
|
||||
}
|
||||
|
||||
}
|
@@ -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);
|
||||
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package cn.iocoder.yudao.module.bpm.service.task.trigger;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.NormalFormTriggerSetting;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
// TODO @jason:改成 BpmFormUpdateTrigger
|
||||
/**
|
||||
* BPM 更新流程表单触发器
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class BpmUpdateNormalFormTrigger implements BpmTrigger {
|
||||
|
||||
@Resource
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
|
||||
@Override
|
||||
public BpmTriggerTypeEnum getType() {
|
||||
return BpmTriggerTypeEnum.UPDATE_NORMAL_FORM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String processInstanceId, String param) {
|
||||
// 1. 解析更新流程表单配置
|
||||
NormalFormTriggerSetting setting = JsonUtils.parseObject(param, NormalFormTriggerSetting.class);
|
||||
if (setting == null) {
|
||||
log.error("[execute][流程({}) 更新流程表单触发器配置为空]", processInstanceId);
|
||||
return;
|
||||
}
|
||||
// 2.更新流程变量
|
||||
if (CollUtil.isNotEmpty(setting.getUpdateFormFields())) {
|
||||
processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -127,8 +127,8 @@ public class CrmBusinessController {
|
||||
}
|
||||
|
||||
@GetMapping("/simple-all-list")
|
||||
@Operation(summary = "获得联系人的精简列表")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
|
||||
@Operation(summary = "获得商机的精简列表")
|
||||
@PreAuthorize("@ss.hasPermission('crm:business:query')")
|
||||
public CommonResult<List<CrmBusinessRespVO>> getSimpleContactList() {
|
||||
CrmBusinessPageReqVO reqVO = new CrmBusinessPageReqVO();
|
||||
reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
|
||||
|
@@ -2,7 +2,7 @@
|
||||
POST {{baseUrl}}/crm/permission/create
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
tenant-id: {{adminTenantId}}
|
||||
|
||||
{
|
||||
"userId": 1,
|
||||
@@ -15,7 +15,7 @@ tenant-id: {{adminTenentId}}
|
||||
PUT {{baseUrl}}/crm/permission/update
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
tenant-id: {{adminTenantId}}
|
||||
|
||||
{
|
||||
"userId": 1,
|
||||
@@ -28,5 +28,5 @@ tenant-id: {{adminTenentId}}
|
||||
### 请求 /delete
|
||||
DELETE {{baseUrl}}/crm/permission/delete?bizType=2&bizId=1&id=1
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
tenant-id: {{adminTenantId}}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user