Commit 4e9583fae4ea6d7de60704dd861694b844624f43

Authored by YunaiV
2 parents b8e56a6b 129e9868

Merge branch 'feature/bpm' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17

Showing 21 changed files with 460 additions and 224 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java
... ... @@ -199,4 +199,12 @@ public class JsonUtils {
199 199 return JSONUtil.isTypeJSON(text);
200 200 }
201 201  
  202 + /**
  203 + * 判断字符串是否为 JSON 类型的字符串
  204 + * @param str 字符串
  205 + */
  206 + public static boolean isJsonObject(String str) {
  207 + return JSONUtil.isTypeJSONObject(str);
  208 + }
  209 +
202 210 }
... ...
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java
... ... @@ -16,7 +16,8 @@ import java.util.Arrays;
16 16 @AllArgsConstructor
17 17 public enum BpmTriggerTypeEnum implements ArrayValuable<Integer> {
18 18  
19   - HTTP_REQUEST(1, "发起 HTTP 请求");
  19 + HTTP_REQUEST(1, "发起 HTTP 请求"),
  20 + UPDATE_NORMAL_FORM(2, "更新流程表单"); // TODO @jason:FORM_UPDATE
20 21  
21 22 /**
22 23 * 触发器执行动作类型
... ...
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java
... ... @@ -32,6 +32,7 @@ public enum BpmReasonEnum {
32 32 ASSIGN_EMPTY_REJECT("审批人为空,自动不通过"),
33 33 APPROVE_TYPE_AUTO_APPROVE("非人工审核,自动通过"),
34 34 APPROVE_TYPE_AUTO_REJECT("非人工审核,自动不通过"),
  35 + CANCEL_BY_PROCESS_CLEAN("进程清理自动取消"),
35 36 ;
36 37  
37 38 private final String reason;
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
1 1 package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple;
2 2  
  3 +import cn.iocoder.yudao.framework.common.core.KeyValue;
3 4 import cn.iocoder.yudao.framework.common.validation.InEnum;
4 5 import cn.iocoder.yudao.module.bpm.enums.definition.*;
5 6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
... ... @@ -157,6 +158,7 @@ public class BpmSimpleModelNodeVO {
157 158 @Schema(description = "值", example = "xxx")
158 159 @NotEmpty(message = "值不能为空")
159 160 private String value;
  161 +
160 162 }
161 163  
162 164 @Schema(description = "审批节点拒绝处理策略")
... ... @@ -343,6 +345,13 @@ public class BpmSimpleModelNodeVO {
343 345 @Valid
344 346 private HttpRequestTriggerSetting httpRequestSetting;
345 347  
  348 + // TODO @jason:这个要不直接叫 formSetting,更好理解一点哈
  349 + // TODO @jason:如果搞成 List<NormalFormTriggerSetting>,是不是可以做条件组了?微信讨论哈
  350 + /**
  351 + * 流程表单触发器设置
  352 + */
  353 + private NormalFormTriggerSetting normalFormSetting;
  354 +
346 355 @Schema(description = "http 请求触发器设置", example = "{}")
347 356 @Data
348 357 public static class HttpRequestTriggerSetting {
... ... @@ -359,6 +368,26 @@ public class BpmSimpleModelNodeVO {
359 368 @Schema(description = "请求头参数设置", example = "[]")
360 369 @Valid
361 370 private List<HttpRequestParam> body;
  371 +
  372 + // TODO @json:可能未来看情况,搞个 HttpResponseParam;得看看有没别的业务需要,抽象统一
  373 + /**
  374 + * 请求返回处理设置,用于修改流程表单值
  375 + * <p>
  376 + * key:表示要修改的流程表单字段名(name)
  377 + * value:接口返回的字段名
  378 + */
  379 + @Schema(description = "请求返回处理设置", example = "[]")
  380 + private List<KeyValue<String, String>> response;
  381 +
  382 + }
  383 +
  384 + @Schema(description = "流程表单触发器设置", example = "{}")
  385 + @Data
  386 + public static class NormalFormTriggerSetting {
  387 +
  388 + @Schema(description = "修改的表单字段", example = "userName")
  389 + private Map<String, Object> updateFormFields;
  390 +
362 391 }
363 392  
364 393 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java
... ... @@ -9,7 +9,10 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 9 import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
10 10 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc.BpmProcessInstanceCopyRespVO;
11 11 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
  12 +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
12 13 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
  14 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
  15 +import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
13 16 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService;
14 17 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
15 18 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
... ... @@ -42,6 +45,8 @@ public class BpmProcessInstanceCopyController {
42 45 private BpmProcessInstanceCopyService processInstanceCopyService;
43 46 @Resource
44 47 private BpmProcessInstanceService processInstanceService;
  48 + @Resource
  49 + private BpmProcessDefinitionService processDefinitionService;
45 50  
46 51 @Resource
47 52 private AdminUserApi adminUserApi;
... ... @@ -62,6 +67,8 @@ public class BpmProcessInstanceCopyController {
62 67 convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId));
63 68 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),
64 69 copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator()))));
  70 + Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
  71 + convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessDefinitionId));
65 72 return success(convertPage(pageResult, copy -> {
66 73 BpmProcessInstanceCopyRespVO copyVO = BeanUtils.toBean(copy, BpmProcessInstanceCopyRespVO.class);
67 74 MapUtils.findAndThen(userMap, Long.valueOf(copy.getCreator()),
... ... @@ -69,7 +76,12 @@ public class BpmProcessInstanceCopyController {
69 76 MapUtils.findAndThen(userMap, copy.getStartUserId(),
70 77 user -> copyVO.setCreateUser(BeanUtils.toBean(user, UserSimpleBaseVO.class)));
71 78 MapUtils.findAndThen(processInstanceMap, copyVO.getProcessInstanceId(),
72   - processInstance -> copyVO.setProcessInstanceStartTime(DateUtils.of(processInstance.getStartTime())));
  79 + processInstance -> {
  80 + copyVO.setSummary(FlowableUtils.getSummary(
  81 + processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),
  82 + processInstance.getProcessVariables()));
  83 + copyVO.setProcessInstanceStartTime(DateUtils.of(processInstance.getStartTime()));
  84 + });
73 85 return copyVO;
74 86 }));
75 87 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java
1 1 package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc;
2 2  
  3 +import cn.iocoder.yudao.framework.common.core.KeyValue;
3 4 import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
4 5 import io.swagger.v3.oas.annotations.media.Schema;
5 6 import lombok.Data;
6 7  
7 8 import java.time.LocalDateTime;
  9 +import java.util.List;
8 10  
9 11 @Schema(description = "管理后台 - 流程实例抄送的分页 Item Response VO")
10 12 @Data
... ... @@ -40,4 +42,7 @@ public class BpmProcessInstanceCopyRespVO {
40 42 @Schema(description = "抄送时间", requiredMode = Schema.RequiredMode.REQUIRED)
41 43 private LocalDateTime createTime;
42 44  
  45 + @Schema(description = "流程摘要", example = "[]")
  46 + private List<KeyValue<String, String>> summary;
  47 +
43 48 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java
... ... @@ -32,7 +32,14 @@ public class BpmProcessInstancePageReqVO extends PageParam {
32 32 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
33 33 private LocalDateTime[] createTime;
34 34  
  35 + @Schema(description = "结束时间")
  36 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  37 + private LocalDateTime[] endTime;
  38 +
35 39 @Schema(description = "发起用户编号", example = "1024")
36 40 private Long startUserId; // 注意,只有在【流程实例】菜单,才使用该参数
37 41  
  42 + @Schema(description = "动态表单字段查询 JSON Str", example = "{}")
  43 + private String formFieldsParams; // SpringMVC 在 get 请求下,无法方便的定义 Map 类型的参数,所以通过 String 接收后,逻辑里面转换
  44 +
38 45 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java
... ... @@ -85,10 +85,6 @@ public class BpmTaskRespVO {
85 85 @Schema(description = "是否填写审批意见", example = "false")
86 86 private Boolean reasonRequire;
87 87  
88   - // TODO @lesan:要不放到 processInstance 里面?因为摘要是流程实例的,不是流程任务的
89   - @Schema(description = "流程摘要", example = "[]")
90   - private List<KeyValue<String, String>> summary; // 只有流程表单,才有摘要!
91   -
92 88 @Data
93 89 @Schema(description = "流程实例")
94 90 public static class ProcessInstance {
... ... @@ -105,6 +101,9 @@ public class BpmTaskRespVO {
105 101 @Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
106 102 private String processDefinitionId;
107 103  
  104 + @Schema(description = "流程摘要", example = "[]")
  105 + private List<KeyValue<String, String>> summary; // 只有流程表单,才有摘要!
  106 +
108 107 /**
109 108 * 发起人的用户信息
110 109 */
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
... ... @@ -80,6 +80,8 @@ public interface BpmProcessInstanceConvert {
80 80 // 摘要
81 81 respVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(respVO.getProcessDefinitionId()),
82 82 pageResult.getList().get(i).getProcessVariables()));
  83 + // 表单
  84 + respVO.setFormVariables(pageResult.getList().get(i).getProcessVariables());
83 85 }
84 86 return vpPageResult;
85 87 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
... ... @@ -54,7 +54,7 @@ public interface BpmTaskConvert {
54 54 AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
55 55 taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
56 56 // 摘要
57   - taskVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),
  57 + taskVO.getProcessInstance().setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),
58 58 processInstance.getProcessVariables()));
59 59 });
60 60 }
... ... @@ -80,7 +80,7 @@ public interface BpmTaskConvert {
80 80 taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
81 81 taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
82 82 // 摘要
83   - taskVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),
  83 + taskVO.getProcessInstance().setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),
84 84 processInstance.getProcessVariables()));
85 85 }
86 86 return taskVO;
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java
... ... @@ -50,6 +50,12 @@ public class BpmProcessInstanceCopyDO extends BaseDO {
50 50 */
51 51 private String processInstanceId;
52 52 /**
  53 + * 流程实例的流程定义编号
  54 + *
  55 + * 关联 ProcessInstance 的 processDefinitionId 属性
  56 + */
  57 + private String processDefinitionId;
  58 + /**
53 59 * 流程分类
54 60 *
55 61 * 冗余 ProcessInstance 的 category 字段
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/redis/BpmProcessIdRedisDAO.java
1 1 package cn.iocoder.yudao.module.bpm.dal.redis;
2 2  
3 3 import cn.hutool.core.date.DateUtil;
  4 +import cn.hutool.core.util.StrUtil;
4 5 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
5 6 import jakarta.annotation.Resource;
6 7 import org.springframework.data.redis.core.StringRedisTemplate;
... ... @@ -48,7 +49,10 @@ public class BpmProcessIdRedisDAO {
48 49 String noPrefix = processIdRule.getPrefix() + infix + processIdRule.getPostfix();
49 50 String key = RedisKeyConstants.BPM_PROCESS_ID + noPrefix;
50 51 Long no = stringRedisTemplate.opsForValue().increment(key);
51   - stringRedisTemplate.expire(key, Duration.ofDays(1L));
  52 + if (StrUtil.isEmpty(infix)) {
  53 + // 特殊:没有前缀,则不能过期,不能每次都是从 0 开始
  54 + stringRedisTemplate.expire(key, Duration.ofDays(1L));
  55 + }
52 56 return noPrefix + String.format("%0" + processIdRule.getLength() + "d", no);
53 57 }
54 58  
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
... ... @@ -64,6 +64,13 @@ public class BpmnModelUtils {
64 64 addExtensionElement(element, name, String.valueOf(value));
65 65 }
66 66  
  67 + public static void addExtensionElementJson(FlowElement element, String name, Object value) {
  68 + if (value == null) {
  69 + return;
  70 + }
  71 + addExtensionElement(element, name, JsonUtils.toJsonString(value));
  72 + }
  73 +
67 74 public static void addExtensionElement(FlowElement element, String name, Map<String, String> attributes) {
68 75 if (attributes == null) {
69 76 return;
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
... ... @@ -26,6 +26,9 @@ import org.flowable.task.api.TaskInfo;
26 26  
27 27 import java.util.*;
28 28 import java.util.concurrent.Callable;
  29 +import java.util.stream.Collectors;
  30 +
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
29 32  
30 33 /**
31 34 * Flowable 相关的工具方法
... ... @@ -193,7 +196,6 @@ public class FlowableUtils {
193 196 BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
194 197 }
195 198  
196   - // TODO @lesan:如果值是 null 的情况,可能要调研下飞书、钉钉,是不是不返回哈!
197 199 /**
198 200 * 获得流程实例的摘要
199 201 *
... ... @@ -206,53 +208,40 @@ public class FlowableUtils {
206 208 */
207 209 public static List<KeyValue<String, String>> getSummary(BpmProcessDefinitionInfoDO processDefinitionInfo,
208 210 Map<String, Object> processVariables) {
209   - // TODO @lesan:建议 if return,减少 { 层级
210   - if (ObjectUtil.isNotNull(processDefinitionInfo)
211   - && BpmModelFormTypeEnum.NORMAL.getType().equals(processDefinitionInfo.getFormType())) {
212   - List<KeyValue<String, String>> summaryList = new ArrayList<>();
213   - // TODO @lesan:可以使用 CollUtils.convertMap 简化工作量哈。
214   - Map<String, BpmFormFieldVO> formFieldsMap = new HashMap<>();
215   - processDefinitionInfo.getFormFields().forEach(formFieldStr -> {
216   - BpmFormFieldVO formField = JsonUtils.parseObject(formFieldStr, BpmFormFieldVO.class);
  211 + // 只有流程表单才会显示摘要!
  212 + if (ObjectUtil.isNull(processDefinitionInfo)
  213 + || !BpmModelFormTypeEnum.NORMAL.getType().equals(processDefinitionInfo.getFormType())) {
  214 + return null;
  215 + }
  216 +
  217 + // 解析表单配置
  218 + Map<String, BpmFormFieldVO> formFieldsMap = new HashMap<>();
  219 + processDefinitionInfo.getFormFields().forEach(formFieldStr -> {
  220 + BpmFormFieldVO formField = JsonUtils.parseObject(formFieldStr, BpmFormFieldVO.class);
  221 + if (formField != null) {
  222 + formFieldsMap.put(formField.getField(), formField);
  223 + }
  224 + });
  225 +
  226 + // 情况一:当自定义了摘要
  227 + if (ObjectUtil.isNotNull(processDefinitionInfo.getSummarySetting())
  228 + && Boolean.TRUE.equals(processDefinitionInfo.getSummarySetting().getEnable())) {
  229 + return convertList(processDefinitionInfo.getSummarySetting().getSummary(), item -> {
  230 + BpmFormFieldVO formField = formFieldsMap.get(item);
217 231 if (formField != null) {
218   - formFieldsMap.put(formField.getField(), formField);
  232 + return new KeyValue<String, String>(formField.getTitle(),
  233 + processVariables.getOrDefault(item, "").toString());
219 234 }
  235 + return null;
220 236 });
221   -
222   - // TODO @lesan:这里也可以 if return,还是为了减少括号哈。这样,就可以写注释,情况一:;情况二:
223   - if (ObjectUtil.isNotNull(processDefinitionInfo.getSummarySetting())
224   - && Boolean.TRUE.equals(processDefinitionInfo.getSummarySetting().getEnable())) {
225   - // TODO @lesan:这里,也可以通过 CollUtils.convertList 简化哈。
226   - for (String item : processDefinitionInfo.getSummarySetting().getSummary()) {
227   - BpmFormFieldVO formField = formFieldsMap.get(item);
228   - if (formField != null) {
229   - summaryList.add(new KeyValue<>(formField.getTitle(),
230   - processVariables.getOrDefault(item, "").toString()));
231   - }
232   - }
233   - } else {
234   - // 默认展示前三个
235   - /* TODO @lesan:stream 简化
236   - * summaryList.addAll(formFieldsMap.entrySet().stream()
237   - * .limit(3)
238   - * .map(entry -> new KeyValue<>(entry.getValue().getTitle(),
239   - * processVariables.getOrDefault(entry.getValue().getField(), "").toString()))
240   - * .collect(Collectors.toList()));
241   - */
242   - int j = 0;
243   - for (Map.Entry<String, BpmFormFieldVO> entry : formFieldsMap.entrySet()) {
244   - BpmFormFieldVO formField = entry.getValue();
245   - if (j > 2) {
246   - break;
247   - }
248   - summaryList.add(new KeyValue<>(formField.getTitle(),
249   - processVariables.getOrDefault(formField.getField(), "").toString()));
250   - j++;
251   - }
252   - }
253   - return summaryList;
254 237 }
255   - return null;
  238 +
  239 + // 情况二:默认摘要展示前三个表单字段
  240 + return formFieldsMap.entrySet().stream()
  241 + .limit(3)
  242 + .map(entry -> new KeyValue<>(entry.getValue().getTitle(),
  243 + processVariables.getOrDefault(entry.getValue().getField(), "").toString()))
  244 + .collect(Collectors.toList());
256 245 }
257 246  
258 247 // ========== Task 相关的工具方法 ==========
... ... @@ -317,9 +306,9 @@ public class FlowableUtils {
317 306  
318 307 private static Object getExpressionValue(VariableContainer variableContainer, String expressionString,
319 308 ProcessEngineConfigurationImpl processEngineConfiguration) {
320   - assert processEngineConfiguration!= null;
  309 + assert processEngineConfiguration != null;
321 310 ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
322   - assert expressionManager!= null;
  311 + assert expressionManager != null;
323 312 Expression expression = expressionManager.createExpression(expressionString);
324 313 return expression.getValue(variableContainer);
325 314 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
... ... @@ -5,7 +5,6 @@ import cn.hutool.core.lang.Assert;
5 5 import cn.hutool.core.map.MapUtil;
6 6 import cn.hutool.core.util.*;
7 7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8   -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
9 8 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
10 9 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups;
11 10 import cn.iocoder.yudao.module.bpm.enums.definition.*;
... ... @@ -626,54 +625,50 @@ public class SimpleModelUtils {
626 625  
627 626 public static SequenceFlow buildSequenceFlow(String sourceId, String targetId,
628 627 BpmSimpleModelNodeVO node) {
629   - String conditionExpression = buildConditionExpression(node);
  628 + String conditionExpression = buildConditionExpression(node.getConditionSetting());
630 629 return buildBpmnSequenceFlow(sourceId, targetId, node.getId(), node.getName(), conditionExpression);
631 630 }
  631 + }
632 632  
633   - /**
634   - * 构造条件表达式
635   - *
636   - * @param node 条件节点
637   - */
638   - public static String buildConditionExpression(BpmSimpleModelNodeVO node) {
639   - return buildConditionExpression(node.getConditionSetting().getConditionType(), node.getConditionSetting().getConditionExpression(),
640   - node.getConditionSetting().getConditionGroups());
641   - }
  633 + /**
  634 + * 构造条件表达式
  635 + */
  636 + public static String buildConditionExpression(BpmSimpleModelNodeVO.ConditionSetting conditionSetting) {
  637 + return buildConditionExpression(conditionSetting.getConditionType(), conditionSetting.getConditionExpression(),
  638 + conditionSetting.getConditionGroups());
  639 + }
642 640  
643   - public static String buildConditionExpression(BpmSimpleModelNodeVO.RouterSetting router) {
644   - return buildConditionExpression(router.getConditionType(), router.getConditionExpression(),
645   - router.getConditionGroups());
646   - }
  641 + public static String buildConditionExpression(BpmSimpleModelNodeVO.RouterSetting routerSetting) {
  642 + return buildConditionExpression(routerSetting.getConditionType(), routerSetting.getConditionExpression(),
  643 + routerSetting.getConditionGroups());
  644 + }
647 645  
648   - public static String buildConditionExpression(Integer conditionType, String conditionExpression,
649   - ConditionGroups conditionGroups) {
650   - BpmSimpleModeConditionTypeEnum conditionTypeEnum = BpmSimpleModeConditionTypeEnum.valueOf(conditionType);
651   - if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.EXPRESSION) {
652   - return conditionExpression;
  646 + public static String buildConditionExpression(Integer conditionType, String conditionExpression, ConditionGroups conditionGroups) {
  647 + BpmSimpleModeConditionTypeEnum conditionTypeEnum = BpmSimpleModeConditionTypeEnum.valueOf(conditionType);
  648 + if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.EXPRESSION) {
  649 + return conditionExpression;
  650 + }
  651 + if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.RULE) {
  652 + if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) {
  653 + return null;
653 654 }
654   - if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.RULE) {
655   - if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) {
656   - return null;
  655 + List<String> strConditionGroups = CollectionUtils.convertList(conditionGroups.getConditions(), item -> {
  656 + if (CollUtil.isEmpty(item.getRules())) {
  657 + return "";
657 658 }
658   - List<String> strConditionGroups = CollectionUtils.convertList(conditionGroups.getConditions(), item -> {
659   - if (CollUtil.isEmpty(item.getRules())) {
660   - return "";
661   - }
662   - // 构造规则表达式
663   - List<String> list = CollectionUtils.convertList(item.getRules(), (rule) -> {
664   - String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide()
665   - : "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号
666   - return String.format(" %s %s var:convertByType(%s,%s)", rule.getLeftSide(), rule.getOpCode(), rule.getLeftSide(), rightSide);
667   - });
668   - // 构造条件组的表达式
669   - Boolean and = item.getAnd();
670   - return "(" + CollUtil.join(list, and ? " && " : " || ") + ")";
  659 + // 构造规则表达式
  660 + List<String> list = CollectionUtils.convertList(item.getRules(), (rule) -> {
  661 + String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide()
  662 + : "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号
  663 + return String.format(" %s %s var:convertByType(%s,%s)", rule.getLeftSide(), rule.getOpCode(), rule.getLeftSide(), rightSide);
671 664 });
672   - return String.format("${%s}", CollUtil.join(strConditionGroups, conditionGroups.getAnd() ? " && " : " || "));
673   - }
674   - return null;
  665 + // 构造条件组的表达式
  666 + Boolean and = item.getAnd();
  667 + return "(" + CollUtil.join(list, and ? " && " : " || ") + ")";
  668 + });
  669 + return String.format("${%s}", CollUtil.join(strConditionGroups, conditionGroups.getAnd() ? " && " : " || "));
675 670 }
676   -
  671 + return null;
677 672 }
678 673  
679 674 public static class DelayTimerNodeConvert implements NodeConvert {
... ... @@ -727,9 +722,10 @@ public class SimpleModelUtils {
727 722 if (node.getTriggerSetting() != null) {
728 723 addExtensionElement(serviceTask, TRIGGER_TYPE, node.getTriggerSetting().getType());
729 724 if (node.getTriggerSetting().getHttpRequestSetting() != null) {
730   - // TODO @jason:加个 addExtensionElementJson 方法,方便设置 JSON 类型的属性
731   - addExtensionElement(serviceTask, TRIGGER_PARAM,
732   - JsonUtils.toJsonString(node.getTriggerSetting().getHttpRequestSetting()));
  725 + addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getHttpRequestSetting());
  726 + }
  727 + if (node.getTriggerSetting().getNormalFormSetting() != null) {
  728 + addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getNormalFormSetting());
733 729 }
734 730 }
735 731 return serviceTask;
... ... @@ -760,7 +756,7 @@ public class SimpleModelUtils {
760 756 }
761 757  
762 758 public static SequenceFlow buildSequenceFlow(String nodeId, BpmSimpleModelNodeVO.RouterSetting router) {
763   - String conditionExpression = ConditionNodeConvert.buildConditionExpression(router);
  759 + String conditionExpression = SimpleModelUtils.buildConditionExpression(router);
764 760 return buildBpmnSequenceFlow(nodeId, router.getNodeId(), null, null, conditionExpression);
765 761 }
766 762  
... ... @@ -804,7 +800,7 @@ public class SimpleModelUtils {
804 800 // 查找满足条件的 BpmSimpleModelNodeVO 节点
805 801 BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(),
806 802 conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())
807   - && evalConditionExpress(variables, conditionNode));
  803 + && evalConditionExpress(variables, conditionNode.getConditionSetting()));
808 804 if (matchConditionNode == null) {
809 805 matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(),
810 806 conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()));
... ... @@ -819,7 +815,7 @@ public class SimpleModelUtils {
819 815 // 查找满足条件的 BpmSimpleModelNodeVO 节点
820 816 Collection<BpmSimpleModelNodeVO> matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(),
821 817 conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())
822   - && evalConditionExpress(variables, conditionNode));
  818 + && evalConditionExpress(variables, conditionNode.getConditionSetting()));
823 819 if (CollUtil.isEmpty(matchConditionNodes)) {
824 820 matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(),
825 821 conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()));
... ... @@ -841,16 +837,17 @@ public class SimpleModelUtils {
841 837 simulateNextNode(currentNode.getChildNode(), variables, resultNodes);
842 838 }
843 839  
844   - public static boolean evalConditionExpress(Map<String, Object> variables, BpmSimpleModelNodeVO conditionNode) {
845   - return BpmnModelUtils.evalConditionExpress(variables, ConditionNodeConvert.buildConditionExpression(conditionNode));
  840 + public static boolean evalConditionExpress(Map<String, Object> variables, BpmSimpleModelNodeVO.ConditionSetting conditionSetting) {
  841 + return BpmnModelUtils.evalConditionExpress(variables, buildConditionExpression(conditionSetting));
846 842 }
847 843  
848 844 // TODO @芋艿:【高】要不要优化下,抽个 HttpUtils
  845 +
849 846 /**
850 847 * 添加 HTTP 请求参数。请求头或者请求体
851 848 *
852   - * @param params HTTP 请求参数
853   - * @param paramSettings HTTP 请求参数设置
  849 + * @param params HTTP 请求参数
  850 + * @param paramSettings HTTP 请求参数设置
854 851 * @param processVariables 流程变量
855 852 */
856 853 public static void addHttpRequestParam(MultiValueMap<String, String> params,
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
... ... @@ -86,6 +86,7 @@ public class BpmModelServiceImpl implements BpmModelService {
86 86 if (StrUtil.isNotEmpty(name)) {
87 87 modelQuery.modelNameLike("%" + name + "%");
88 88 }
  89 + modelQuery.modelTenantId(FlowableUtils.getTenantId());
89 90 return modelQuery.list();
90 91 }
91 92  
... ... @@ -288,8 +289,7 @@ public class BpmModelServiceImpl implements BpmModelService {
288 289 // 2.3 清理所有 Task
289 290 List<Task> tasks = taskService.createTaskQuery()
290 291 .processDefinitionKey(model.getKey()).list();
291   - // TODO @lesan:貌似传递一个 reason 会好点!
292   - tasks.forEach(task -> taskService.deleteTask(task.getId()));
  292 + tasks.forEach(task -> taskService.deleteTask(task.getId(),BpmReasonEnum.CANCEL_BY_PROCESS_CLEAN.getReason()));
293 293 }
294 294  
295 295 @Override
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java
... ... @@ -77,7 +77,8 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
77 77 .setUserId(userId).setReason(reason).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
78 78 .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
79 79 .setCategory(processDefinition.getCategory()).setTaskId(taskId)
80   - .setActivityId(activityId).setActivityName(activityName));
  80 + .setActivityId(activityId).setActivityName(activityName)
  81 + .setProcessDefinitionId(processInstance.getProcessDefinitionId()));
81 82 processInstanceCopyMapper.insertBatch(copyList);
82 83 }
83 84  
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
... ... @@ -157,4 +157,12 @@ public interface BpmProcessInstanceService {
157 157 */
158 158 void processProcessInstanceCompleted(ProcessInstance instance);
159 159  
  160 + /**
  161 + * 更新 ProcessInstance 的变量
  162 + *
  163 + * @param id 流程编号
  164 + * @param variables 流程变量
  165 + */
  166 + void updateProcessInstanceVariables(String id, Map<String, Object> variables);
  167 +
160 168 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
... ... @@ -133,56 +133,18 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
133 133  
134 134 @Override
135 135 public HistoricProcessInstance getHistoricProcessInstance(String id) {
136   - return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult();
  136 + return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables()
  137 + .singleResult();
137 138 }
138 139  
139 140 @Override
140 141 public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) {
141   - return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).includeProcessVariables().list();
  142 + return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).includeProcessVariables()
  143 + .list();
142 144 }
143 145  
144   - @Override
145   - public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId,
146   - BpmProcessInstancePageReqVO pageReqVO) {
147   - // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
148   - HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery()
149   - .includeProcessVariables()
150   - .processInstanceTenantId(FlowableUtils.getTenantId())
151   - .orderByProcessInstanceStartTime().desc();
152   - if (userId != null) { // 【我的流程】菜单时,需要传递该字段
153   - processInstanceQuery.startedBy(String.valueOf(userId));
154   - } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段
155   - processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId()));
156   - }
157   - if (StrUtil.isNotEmpty(pageReqVO.getName())) {
158   - processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%");
159   - }
160   - if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionKey())) {
161   - processInstanceQuery.processDefinitionKey(pageReqVO.getProcessDefinitionKey());
162   - }
163   - if (StrUtil.isNotEmpty(pageReqVO.getCategory())) {
164   - processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory());
165   - }
166   - if (pageReqVO.getStatus() != null) {
167   - processInstanceQuery.variableValueEquals(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus());
168   - }
169   - if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) {
170   - processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0]));
171   - processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1]));
172   - }
173   - // 查询数量
174   - long processInstanceCount = processInstanceQuery.count();
175   - if (processInstanceCount == 0) {
176   - return PageResult.empty(processInstanceCount);
177   - }
178   - // 查询列表
179   - List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize());
180   - return new PageResult<>(processInstanceList, processInstanceCount);
181   - }
182   -
183   -
184 146 private Map<String, String> getFormFieldsPermission(BpmnModel bpmnModel,
185   - String activityId, String taskId) {
  147 + String activityId, String taskId) {
186 148 // 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id
187 149 if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) {
188 150 activityId = Optional.ofNullable(taskService.getHistoricTask(taskId))
... ... @@ -215,8 +177,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
215 177 }
216 178 // 1.3 读取其它相关数据
217 179 ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(
218   - historicProcessInstance != null ? historicProcessInstance.getProcessDefinitionId() : reqVO.getProcessDefinitionId());
219   - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinition.getId());
  180 + historicProcessInstance != null ? historicProcessInstance.getProcessDefinitionId()
  181 + : reqVO.getProcessDefinitionId());
  182 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService
  183 + .getProcessDefinitionInfo(processDefinition.getId());
220 184 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());
221 185  
222 186 // 2.1 已结束 + 进行中的活动节点
... ... @@ -225,24 +189,29 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
225 189 List<HistoricActivityInstance> activities = null; // 流程实例列表
226 190 if (reqVO.getProcessInstanceId() != null) {
227 191 activities = taskService.getActivityListByProcessInstanceId(reqVO.getProcessInstanceId());
228   - List<HistoricTaskInstance> tasks = taskService.getTaskListByProcessInstanceId(reqVO.getProcessInstanceId(), true);
  192 + List<HistoricTaskInstance> tasks = taskService.getTaskListByProcessInstanceId(reqVO.getProcessInstanceId(),
  193 + true);
229 194 endActivityNodes = getEndActivityNodeList(startUserId, bpmnModel, processDefinitionInfo,
230 195 historicProcessInstance, processInstanceStatus, activities, tasks);
231   - runActivityNodes = getRunApproveNodeList(startUserId, bpmnModel, processDefinition, processVariables, activities, tasks);
  196 + runActivityNodes = getRunApproveNodeList(startUserId, bpmnModel, processDefinition, processVariables,
  197 + activities, tasks);
232 198 }
233 199  
234 200 // 2.2 流程已经结束,直接 return,无需预测
235 201 if (BpmProcessInstanceStatusEnum.isProcessEndStatus(processInstanceStatus)) {
236   - return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance,
  202 + return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo,
  203 + historicProcessInstance,
237 204 processInstanceStatus, endActivityNodes, runActivityNodes, null, null);
238 205 }
239 206  
240 207 // 3.1 计算当前登录用户的待办任务
241   - // TODO @jason:有一个极端情况,如果一个用户有 2 个 task A 和 B,A 已经通过,B 需要审核。这个时,通过 A 进来,todo 拿到 B,会不会表单权限不一致哈。
  208 + // TODO @jason:有一个极端情况,如果一个用户有 2 个 task A 和 B,A 已经通过,B 需要审核。这个时,通过 A 进来,todo 拿到
  209 + // B,会不会表单权限不一致哈。
242 210 BpmTaskRespVO todoTask = taskService.getFirstTodoTask(loginUserId, reqVO.getProcessInstanceId());
243 211  
244 212 // 3.2 预测未运行节点的审批信息
245   - List<ActivityNode> simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo,
  213 + List<ActivityNode> simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel,
  214 + processDefinitionInfo,
246 215 processVariables, activities);
247 216  
248 217 // 4. 拼接最终数据
... ... @@ -250,34 +219,93 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
250 219 processInstanceStatus, endActivityNodes, runActivityNodes, simulateActivityNodes, todoTask);
251 220 }
252 221  
  222 + @Override
  223 + @SuppressWarnings("unchecked")
  224 + public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId,
  225 + BpmProcessInstancePageReqVO pageReqVO) {
  226 + // 1. 构建查询条件
  227 + HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery()
  228 + .includeProcessVariables()
  229 + .processInstanceTenantId(FlowableUtils.getTenantId())
  230 + .orderByProcessInstanceStartTime().desc();
  231 + if (userId != null) { // 【我的流程】菜单时,需要传递该字段
  232 + processInstanceQuery.startedBy(String.valueOf(userId));
  233 + } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段
  234 + processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId()));
  235 + }
  236 + if (StrUtil.isNotEmpty(pageReqVO.getName())) {
  237 + processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%");
  238 + }
  239 + if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionKey())) {
  240 + processInstanceQuery.processDefinitionKey(pageReqVO.getProcessDefinitionKey());
  241 + }
  242 + if (StrUtil.isNotEmpty(pageReqVO.getCategory())) {
  243 + processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory());
  244 + }
  245 + if (pageReqVO.getStatus() != null) {
  246 + processInstanceQuery.variableValueEquals(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
  247 + pageReqVO.getStatus());
  248 + }
  249 + if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) {
  250 + processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0]));
  251 + processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1]));
  252 + }
  253 + if (ArrayUtil.isNotEmpty(pageReqVO.getEndTime())) {
  254 + processInstanceQuery.finishedAfter(DateUtils.of(pageReqVO.getEndTime()[0]));
  255 + processInstanceQuery.finishedBefore(DateUtils.of(pageReqVO.getEndTime()[1]));
  256 + }
  257 + // 表单字段查询
  258 + Map<String, Object> formFieldsParams = JsonUtils.parseObject(pageReqVO.getFormFieldsParams(), Map.class);
  259 + if (CollUtil.isNotEmpty(formFieldsParams)) {
  260 + formFieldsParams.forEach((key, value) -> {
  261 + if (StrUtil.isEmpty(String.valueOf(value))) {
  262 + return;
  263 + }
  264 + // TODO @lesan:应支持多种类型的查询方式,目前只有字符串全等
  265 + processInstanceQuery.variableValueEquals(key, value);
  266 + });
  267 + }
  268 +
  269 + // 2.1 查询数量
  270 + long processInstanceCount = processInstanceQuery.count();
  271 + if (processInstanceCount == 0) {
  272 + return PageResult.empty(processInstanceCount);
  273 + }
  274 + // 2.2 查询列表
  275 + List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO),
  276 + pageReqVO.getPageSize());
  277 + return new PageResult<>(processInstanceList, processInstanceCount);
  278 + }
  279 +
253 280 /**
254 281 * 拼接审批详情的最终数据
255 282 * <p>
256 283 * 主要是,拼接审批人的用户信息、部门信息
257 284 */
258 285 private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO,
259   - BpmnModel bpmnModel,
260   - ProcessDefinition processDefinition,
261   - BpmProcessDefinitionInfoDO processDefinitionInfo,
262   - HistoricProcessInstance processInstance,
263   - Integer processInstanceStatus,
264   - List<ActivityNode> endApprovalNodeInfos,
265   - List<ActivityNode> runningApprovalNodeInfos,
266   - List<ActivityNode> simulateApprovalNodeInfos,
267   - BpmTaskRespVO todoTask) {
  286 + BpmnModel bpmnModel,
  287 + ProcessDefinition processDefinition,
  288 + BpmProcessDefinitionInfoDO processDefinitionInfo,
  289 + HistoricProcessInstance processInstance,
  290 + Integer processInstanceStatus,
  291 + List<ActivityNode> endApprovalNodeInfos,
  292 + List<ActivityNode> runningApprovalNodeInfos,
  293 + List<ActivityNode> simulateApprovalNodeInfos,
  294 + BpmTaskRespVO todoTask) {
268 295 // 1. 获取所有需要读取用户信息的 userIds
269   - List<ActivityNode> approveNodes = newArrayList(asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos));
  296 + List<ActivityNode> approveNodes = newArrayList(
  297 + asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos));
270 298 Set<Long> userIds = BpmProcessInstanceConvert.INSTANCE.parseUserIds(processInstance, approveNodes, todoTask);
271 299 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
272 300 Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
273 301  
274   -
275 302 // 2. 表单权限
276 303 String taskId = reqVO.getTaskId() == null && todoTask != null ? todoTask.getId() : reqVO.getTaskId();
277 304 Map<String, String> formFieldsPermission = getFormFieldsPermission(bpmnModel, reqVO.getActivityId(), taskId);
278 305  
279 306 // 3. 拼接数据
280   - return BpmProcessInstanceConvert.INSTANCE.buildApprovalDetail(bpmnModel, processDefinition, processDefinitionInfo, processInstance,
  307 + return BpmProcessInstanceConvert.INSTANCE.buildApprovalDetail(bpmnModel, processDefinition,
  308 + processDefinitionInfo, processInstance,
281 309 processInstanceStatus, approveNodes, todoTask, formFieldsPermission, userMap, deptMap);
282 310 }
283 311  
... ... @@ -285,17 +313,19 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
285 313 * 获得【已结束】的活动节点们
286 314 */
287 315 private List<ActivityNode> getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel,
288   - BpmProcessDefinitionInfoDO processDefinitionInfo,
289   - HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus,
290   - List<HistoricActivityInstance> activities, List<HistoricTaskInstance> tasks) {
  316 + BpmProcessDefinitionInfoDO processDefinitionInfo,
  317 + HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus,
  318 + List<HistoricActivityInstance> activities, List<HistoricTaskInstance> tasks) {
291 319 // 遍历 tasks 列表,只处理已结束的 UserTask
292   - // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities 的话,它无法成为一个节点
  320 + // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities
  321 + // 的话,它无法成为一个节点
293 322 List<HistoricTaskInstance> endTasks = filterList(tasks, task -> task.getEndTime() != null);
294 323 List<ActivityNode> approvalNodes = convertList(endTasks, task -> {
295 324 FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
296 325 ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName())
297   - .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) ?
298   - BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() : BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
  326 + .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey())
  327 + ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType()
  328 + : BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
299 329 .setStatus(FlowableUtils.getTaskStatus(task))
300 330 .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode))
301 331 .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime()))
... ... @@ -320,7 +350,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
320 350 .setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName())
321 351 .setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType())
322 352 .setStatus(startTask.getStatus()).setTasks(ListUtil.of(startTask))
323   - .setStartTime(DateUtils.of(activity.getStartTime())).setEndTime(DateUtils.of(activity.getEndTime()));
  353 + .setStartTime(DateUtils.of(activity.getStartTime()))
  354 + .setEndTime(DateUtils.of(activity.getEndTime()));
324 355 approvalNodes.add(0, startNode);
325 356 return;
326 357 }
... ... @@ -333,7 +364,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
333 364 ActivityNode endNode = new ActivityNode().setId(activity.getId())
334 365 .setName(BpmSimpleModelNodeTypeEnum.END_NODE.getName())
335 366 .setNodeType(BpmSimpleModelNodeTypeEnum.END_NODE.getType()).setStatus(processInstanceStatus)
336   - .setStartTime(DateUtils.of(activity.getStartTime())).setEndTime(DateUtils.of(activity.getEndTime()));
  367 + .setStartTime(DateUtils.of(activity.getStartTime()))
  368 + .setEndTime(DateUtils.of(activity.getEndTime()));
337 369 String reason = FlowableUtils.getProcessInstanceReason(historicProcessInstance);
338 370 if (StrUtil.isNotEmpty(reason)) {
339 371 endNode.setTasks(singletonList(new ActivityNodeTask().setId(endNode.getId())
... ... @@ -349,15 +381,16 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
349 381 * 获得【进行中】的活动节点们
350 382 */
351 383 private List<ActivityNode> getRunApproveNodeList(Long startUserId,
352   - BpmnModel bpmnModel,
353   - ProcessDefinition processDefinition,
354   - Map<String, Object> processVariables,
355   - List<HistoricActivityInstance> activities,
356   - List<HistoricTaskInstance> tasks) {
  384 + BpmnModel bpmnModel,
  385 + ProcessDefinition processDefinition,
  386 + Map<String, Object> processVariables,
  387 + List<HistoricActivityInstance> activities,
  388 + List<HistoricTaskInstance> tasks) {
357 389 // 构建运行中的任务,基于 activityId 分组
358 390 List<HistoricActivityInstance> runActivities = filterList(activities, activity -> activity.getEndTime() == null
359 391 && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER)));
360   - Map<String, List<HistoricActivityInstance>> runningTaskMap = convertMultiMap(runActivities, HistoricActivityInstance::getActivityId);
  392 + Map<String, List<HistoricActivityInstance>> runningTaskMap = convertMultiMap(runActivities,
  393 + HistoricActivityInstance::getActivityId);
361 394  
362 395 // 按照 activityId 分组,构建 ApprovalNodeInfo 节点
363 396 Map<String, HistoricTaskInstance> taskMap = convertMap(tasks, HistoricTaskInstance::getId);
... ... @@ -367,8 +400,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
367 400 // 构建活动节点
368 401 FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, activityId);
369 402 HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同
370   - ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()).setName(firstActivity.getActivityName())
371   - .setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()).setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
  403 + ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId())
  404 + .setName(firstActivity.getActivityName())
  405 + .setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
  406 + .setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
372 407 .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode))
373 408 .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime()))
374 409 .setTasks(new ArrayList<>());
... ... @@ -381,7 +416,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
381 416 taskService.getAllChildrenTaskListByParentTaskId(activity.getTaskId(), tasks),
382 417 childTask -> childTask.getEndTime() == null);
383 418 if (CollUtil.isNotEmpty(childrenTasks)) {
384   - activityNode.getTasks().addAll(convertList(childrenTasks, BpmProcessInstanceConvert.INSTANCE::buildApprovalTaskInfo));
  419 + activityNode.getTasks().addAll(
  420 + convertList(childrenTasks, BpmProcessInstanceConvert.INSTANCE::buildApprovalTaskInfo));
385 421 }
386 422 }
387 423 // 处理每个任务的 candidateUsers 属性:如果是依次审批,需要预测它的后续审批人。因为 Task 是审批完一个,创建一个新的 Task
... ... @@ -391,8 +427,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
391 427 // 截取当前审批人位置后面的候选人,不包含当前审批人
392 428 ActivityNodeTask approvalTaskInfo = CollUtil.getFirst(activityNode.getTasks());
393 429 Assert.notNull(approvalTaskInfo, "任务不能为空");
394   - int index = CollUtil.indexOf(candidateUserIds, userId -> ObjectUtils.equalsAny(userId, approvalTaskInfo.getOwner(),
395   - approvalTaskInfo.getAssignee())); // 委派或者向前加签情况,需要先比较 owner
  430 + int index = CollUtil.indexOf(candidateUserIds,
  431 + userId -> ObjectUtils.equalsAny(userId, approvalTaskInfo.getOwner(),
  432 + approvalTaskInfo.getAssignee())); // 委派或者向前加签情况,需要先比较 owner
396 433 activityNode.setCandidateUserIds(CollUtil.sub(candidateUserIds, index + 1, candidateUserIds.size()));
397 434 }
398 435 return activityNode;
... ... @@ -403,10 +440,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
403 440 * 获得【预测(未来)】的活动节点们
404 441 */
405 442 private List<ActivityNode> getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel,
406   - BpmProcessDefinitionInfoDO processDefinitionInfo,
407   - Map<String, Object> processVariables,
408   - List<HistoricActivityInstance> activities) {
409   - // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance 包括了历史的操作,不是只有 startEvent 到当前节点的记录
  443 + BpmProcessDefinitionInfoDO processDefinitionInfo,
  444 + Map<String, Object> processVariables,
  445 + List<HistoricActivityInstance> activities) {
  446 + // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance
  447 + // 包括了历史的操作,不是只有 startEvent 到当前节点的记录
410 448 Set<String> runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId);
411 449 // 情况一:BPMN 设计器
412 450 if (Objects.equals(BpmModelTypeEnum.BPMN.getType(), processDefinitionInfo.getModelType())) {
... ... @@ -416,7 +454,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
416 454 }
417 455 // 情况二:SIMPLE 设计器
418 456 if (Objects.equals(BpmModelTypeEnum.SIMPLE.getType(), processDefinitionInfo.getModelType())) {
419   - BpmSimpleModelNodeVO simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(), BpmSimpleModelNodeVO.class);
  457 + BpmSimpleModelNodeVO simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(),
  458 + BpmSimpleModelNodeVO.class);
420 459 List<BpmSimpleModelNodeVO> simpleNodes = SimpleModelUtils.simulateProcess(simpleModel, processVariables);
421 460 return convertList(simpleNodes, simpleNode -> buildNotRunApproveNodeForSimple(startUserId, bpmnModel,
422 461 processDefinitionInfo, processVariables, simpleNode, runActivityIds));
... ... @@ -425,9 +464,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
425 464 }
426 465  
427 466 private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel,
428   - BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
429   - BpmSimpleModelNodeVO node, Set<String> runActivityIds) {
430   - // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance 包括了历史的操作,不是只有 startEvent 到当前节点的记录
  467 + BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
  468 + BpmSimpleModelNodeVO node, Set<String> runActivityIds) {
  469 + // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance
  470 + // 包括了历史的操作,不是只有 startEvent 到当前节点的记录
431 471 if (runActivityIds.contains(node.getId())) {
432 472 return null;
433 473 }
... ... @@ -460,12 +500,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
460 500 }
461 501  
462 502 private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel,
463   - BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
464   - FlowElement node, Set<String> runActivityIds) {
  503 + BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
  504 + FlowElement node, Set<String> runActivityIds) {
465 505 if (runActivityIds.contains(node.getId())) {
466 506 return null;
467 507 }
468   - ActivityNode activityNode = new ActivityNode().setId(node.getId()).setStatus(BpmTaskStatusEnum.NOT_START.getStatus());
  508 + ActivityNode activityNode = new ActivityNode().setId(node.getId())
  509 + .setStatus(BpmTaskStatusEnum.NOT_START.getStatus());
469 510  
470 511 // 1. 开始节点
471 512 if (node instanceof StartEvent) {
... ... @@ -491,7 +532,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
491 532 }
492 533  
493 534 private List<Long> getTaskCandidateUserList(BpmnModel bpmnModel, String activityId,
494   - Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
  535 + Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
495 536 Set<Long> userIds = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId,
496 537 startUserId, processDefinitionId, processVariables);
497 538 return new ArrayList<>(userIds);
... ... @@ -505,14 +546,16 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
505 546 return null;
506 547 }
507 548 // 1.2 获得流程定义
508   - BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId());
  549 + BpmnModel bpmnModel = processDefinitionService
  550 + .getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId());
509 551 if (bpmnModel == null) {
510 552 return null;
511 553 }
512 554 BpmSimpleModelNodeVO simpleModel = null;
513 555 BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(
514 556 processInstance.getProcessDefinitionId());
515   - if (processDefinitionInfo != null && BpmModelTypeEnum.SIMPLE.getType().equals(processDefinitionInfo.getModelType())) {
  557 + if (processDefinitionInfo != null
  558 + && BpmModelTypeEnum.SIMPLE.getType().equals(processDefinitionInfo.getModelType())) {
516 559 simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(), BpmSimpleModelNodeVO.class);
517 560 }
518 561 // 1.3 获得流程实例对应的活动实例列表 + 任务列表
... ... @@ -524,10 +567,12 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
524 567 activityInstance -> activityInstance.getEndTime() == null);
525 568 Set<String> finishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId,
526 569 activityInstance -> activityInstance.getEndTime() != null
527   - && ObjectUtil.notEqual(activityInstance.getActivityType(), BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW));
  570 + && ObjectUtil.notEqual(activityInstance.getActivityType(),
  571 + BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW));
528 572 Set<String> finishedSequenceFlowActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId,
529 573 activityInstance -> activityInstance.getEndTime() != null
530   - && ObjectUtil.equals(activityInstance.getActivityType(), BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW));
  574 + && ObjectUtil.equals(activityInstance.getActivityType(),
  575 + BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW));
531 576 // 特殊:会签情况下,会有部分已完成(审批)、部分未完成(待审批),此时需要 finishedTaskActivityIds 移除掉
532 577 finishedTaskActivityIds.removeAll(unfinishedTaskActivityIds);
533 578 // 特殊:如果流程实例被拒绝,则需要计算是哪个活动节点。
... ... @@ -545,8 +590,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
545 590 Set<Long> userIds = BpmProcessInstanceConvert.INSTANCE.parseUserIds02(processInstance, tasks);
546 591 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
547 592 Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
548   - return BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceBpmnModelView(processInstance, tasks, bpmnModel, simpleModel,
549   - unfinishedTaskActivityIds, finishedTaskActivityIds, finishedSequenceFlowActivityIds, rejectTaskActivityIds,
  593 + return BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceBpmnModelView(processInstance, tasks, bpmnModel,
  594 + simpleModel,
  595 + unfinishedTaskActivityIds, finishedTaskActivityIds, finishedSequenceFlowActivityIds,
  596 + rejectTaskActivityIds,
550 597 userMap, deptMap);
551 598 }
552 599  
... ... @@ -556,7 +603,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
556 603 @Transactional(rollbackFor = Exception.class)
557 604 public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
558 605 // 获得流程定义
559   - ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
  606 + ProcessDefinition definition = processDefinitionService
  607 + .getProcessDefinition(createReqVO.getProcessDefinitionId());
560 608 // 发起流程
561 609 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null,
562 610 createReqVO.getStartUserSelectAssignees());
... ... @@ -566,16 +614,18 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
566 614 public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
567 615 return FlowableUtils.executeAuthenticatedUserId(userId, () -> {
568 616 // 获得流程定义
569   - ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
  617 + ProcessDefinition definition = processDefinitionService
  618 + .getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
570 619 // 发起流程
571   - return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(),
  620 + return createProcessInstance0(userId, definition, createReqDTO.getVariables(),
  621 + createReqDTO.getBusinessKey(),
572 622 createReqDTO.getStartUserSelectAssignees());
573 623 });
574 624 }
575 625  
576 626 private String createProcessInstance0(Long userId, ProcessDefinition definition,
577   - Map<String, Object> variables, String businessKey,
578   - Map<String, List<Long>> startUserSelectAssignees) {
  627 + Map<String, Object> variables, String businessKey,
  628 + Map<String, List<Long>> startUserSelectAssignees) {
579 629 // 1.1 校验流程定义
580 630 if (definition == null) {
581 631 throw exception(PROCESS_DEFINITION_NOT_EXISTS);
... ... @@ -583,7 +633,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
583 633 if (definition.isSuspended()) {
584 634 throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
585 635 }
586   - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(definition.getId());
  636 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService
  637 + .getProcessDefinitionInfo(definition.getId());
587 638 if (processDefinitionInfo == null) {
588 639 throw exception(PROCESS_DEFINITION_NOT_EXISTS);
589 640 }
... ... @@ -602,9 +653,12 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
602 653 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量,发起人 ID
603 654 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中
604 655 BpmProcessInstanceStatusEnum.RUNNING.getStatus());
605   - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 true,不影响没配置 skipExpression 的节点
  656 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为
  657 + // true,不影响没配置
  658 + // skipExpression 的节点
606 659 if (CollUtil.isNotEmpty(startUserSelectAssignees)) {
607   - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees);
  660 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,
  661 + startUserSelectAssignees);
608 662 }
609 663  
610 664 // 3. 创建流程
... ... @@ -634,7 +688,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
634 688 return instance.getId();
635 689 }
636 690  
637   - private void validateStartUserSelectAssignees(ProcessDefinition definition, Map<String, List<Long>> startUserSelectAssignees) {
  691 + private void validateStartUserSelectAssignees(ProcessDefinition definition,
  692 + Map<String, List<Long>> startUserSelectAssignees) {
638 693 // 1. 获得发起人自选审批人的 UserTask/ServiceTask 列表
639 694 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId());
640 695 List<Task> tasks = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectTaskList(bpmnModel);
... ... @@ -669,7 +724,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
669 724 throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
670 725 }
671 726 // 1.3 校验允许撤销审批中的申请
672   - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(instance.getProcessDefinitionId());
  727 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService
  728 + .getProcessDefinitionInfo(instance.getProcessDefinitionId());
673 729 Assert.notNull(processDefinitionInfo, "流程定义({})不存在", processDefinitionInfo);
674 730 if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消
675 731 && Boolean.FALSE.equals(processDefinitionInfo.getAllowCancelRunningProcess())) {
... ... @@ -707,9 +763,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
707 763  
708 764 @Override
709 765 public void updateProcessInstanceReject(ProcessInstance processInstance, String reason) {
710   - runtimeService.setVariable(processInstance.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
  766 + runtimeService.setVariable(processInstance.getProcessInstanceId(),
  767 + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
711 768 BpmProcessInstanceStatusEnum.REJECT.getStatus());
712   - runtimeService.setVariable(processInstance.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON,
  769 + runtimeService.setVariable(processInstance.getProcessInstanceId(),
  770 + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON,
713 771 BpmReasonEnum.REJECT_TASK.format(reason));
714 772 }
715 773  
... ... @@ -720,18 +778,22 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
720 778 // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号
721 779 FlowableUtils.execute(instance.getTenantId(), () -> {
722 780 // 1.1 获取当前状态
723   - Integer status = (Integer) instance.getProcessVariables().get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
724   - String reason = (String) instance.getProcessVariables().get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON);
  781 + Integer status = (Integer) instance.getProcessVariables()
  782 + .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
  783 + String reason = (String) instance.getProcessVariables()
  784 + .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON);
725 785 // 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态
726 786 // 为什么这么处理?因为流程完成,并且完成了,说明审批通过了
727 787 if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) {
728 788 status = BpmProcessInstanceStatusEnum.APPROVE.getStatus();
729   - runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, status);
  789 + runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
  790 + status);
730 791 }
731 792  
732 793 // 2. 发送对应的消息通知
733 794 if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
734   - messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance));
  795 + messageService.sendMessageWhenProcessInstanceApprove(
  796 + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance));
735 797 } else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) {
736 798 messageService.sendMessageWhenProcessInstanceReject(
737 799 BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason));
... ... @@ -743,4 +805,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
743 805 });
744 806 }
745 807  
  808 + @Override
  809 + public void updateProcessInstanceVariables(String id, Map<String, Object> variables) {
  810 + runtimeService.setVariables(id, variables);
  811 + }
  812 +
746 813 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmHttpRequestTrigger.java
1 1 package cn.iocoder.yudao.module.bpm.service.task.trigger;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.core.KeyValue;
  6 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
3 7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
4 8 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting;
5 9 import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
6 10 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
7 11 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
  12 +import com.fasterxml.jackson.core.type.TypeReference;
8 13 import jakarta.annotation.Resource;
9 14 import lombok.extern.slf4j.Slf4j;
10 15 import org.flowable.engine.runtime.ProcessInstance;
... ... @@ -17,6 +22,8 @@ import org.springframework.util.MultiValueMap;
17 22 import org.springframework.web.client.RestClientException;
18 23 import org.springframework.web.client.RestTemplate;
19 24  
  25 +import java.util.HashMap;
  26 +import java.util.List;
20 27 import java.util.Map;
21 28  
22 29 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
... ... @@ -63,13 +70,55 @@ public class BpmHttpRequestTrigger implements BpmTrigger {
63 70 // TODO @芋艿:要不要抽象一个 Http 请求的工具类,方便复用呢?
64 71 // 3. 发起请求
65 72 HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
  73 + ResponseEntity<String> responseEntity;
66 74 try {
67   - ResponseEntity<String> responseEntity = restTemplate.exchange(setting.getUrl(), HttpMethod.POST,
  75 + responseEntity = restTemplate.exchange(setting.getUrl(), HttpMethod.POST,
68 76 requestEntity, String.class);
69 77 log.info("[execute][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity);
70 78 } catch (RestClientException e) {
71 79 log.error("[execute][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage());
  80 + return;
  81 + }
  82 +
  83 + // 4.1 判断是否需要解析返回值
  84 + if (StrUtil.isEmpty(responseEntity.getBody())
  85 + || !responseEntity.getStatusCode().is2xxSuccessful()
  86 + || CollUtil.isEmpty(setting.getResponse())) {
  87 + return;
  88 + }
  89 + // 4.2 解析返回值, 返回值必须符合 CommonResult 规范。
  90 + CommonResult<Map<String, Object>> respResult = JsonUtils.parseObjectQuietly(
  91 + responseEntity.getBody(), new TypeReference<>() {});
  92 + if (respResult == null || !respResult.isSuccess()){
  93 + return;
  94 + }
  95 + // 4.3 获取需要更新的流程变量
  96 + Map<String, Object> updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), setting.getResponse());
  97 + // 4.4 更新流程变量
  98 + if (CollUtil.isNotEmpty(updateVariables)) {
  99 + processInstanceService.updateProcessInstanceVariables(processInstanceId, updateVariables);
  100 + }
  101 + }
  102 +
  103 + /**
  104 + * 从请求返回值获取需要更新的流程变量
  105 + *
  106 + * @param result 请求返回结果
  107 + * @param responseSettings 返回设置
  108 + * @return 需要更新的流程变量
  109 + */
  110 + private Map<String, Object> getNeedUpdatedVariablesFromResponse(Map<String,Object> result,
  111 + List<KeyValue<String, String>> responseSettings) {
  112 + Map<String, Object> updateVariables = new HashMap<>();
  113 + if (CollUtil.isEmpty(result)) {
  114 + return updateVariables;
72 115 }
  116 + responseSettings.forEach(responseSetting -> {
  117 + if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) {
  118 + updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue()));
  119 + }
  120 + });
  121 + return updateVariables;
73 122 }
74 123  
75 124 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmUpdateNormalFormTrigger.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.task.trigger;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  5 +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.NormalFormTriggerSetting;
  6 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
  7 +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
  8 +import jakarta.annotation.Resource;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.springframework.stereotype.Component;
  11 +
  12 +// TODO @jason:改成 BpmFormUpdateTrigger
  13 +/**
  14 + * BPM 更新流程表单触发器
  15 + *
  16 + * @author jason
  17 + */
  18 +@Component
  19 +@Slf4j
  20 +public class BpmUpdateNormalFormTrigger implements BpmTrigger {
  21 +
  22 + @Resource
  23 + private BpmProcessInstanceService processInstanceService;
  24 +
  25 + @Override
  26 + public BpmTriggerTypeEnum getType() {
  27 + return BpmTriggerTypeEnum.UPDATE_NORMAL_FORM;
  28 + }
  29 +
  30 + @Override
  31 + public void execute(String processInstanceId, String param) {
  32 + // 1. 解析更新流程表单配置
  33 + NormalFormTriggerSetting setting = JsonUtils.parseObject(param, NormalFormTriggerSetting.class);
  34 + if (setting == null) {
  35 + log.error("[execute][流程({}) 更新流程表单触发器配置为空]", processInstanceId);
  36 + return;
  37 + }
  38 + // 2.更新流程变量
  39 + if (CollUtil.isNotEmpty(setting.getUpdateFormFields())) {
  40 + processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields());
  41 + }
  42 + }
  43 +
  44 +}
... ...