Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into develop
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -27,4 +27,4 @@ public class BpmProcessExpressionRespVO {
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public interface ErrorCodeConstants {
|
||||
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, "流程取消失败,该流程不允许取消");
|
||||
ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败");
|
||||
ErrorCode PROCESS_INSTANCE_HTTP_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 请求调用失败");
|
||||
ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置");
|
||||
ErrorCode PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_008, "子流程取消失败,子流程不允许取消");
|
||||
|
||||
@@ -58,7 +58,6 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务");
|
||||
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, "审批意见不能为空!");
|
||||
ErrorCode TASK_WITHDRAW_FAIL_PROCESS_NOT_RUNNING = new ErrorCode(1_009_005_017, "撤回失败,流程实例未运行!");
|
||||
|
||||
@@ -7,44 +7,35 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCand
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||
import lombok.Setter;
|
||||
import org.flowable.bpmn.model.Activity;
|
||||
import org.flowable.bpmn.model.CallActivity;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.bpmn.model.*;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
|
||||
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 自定义的【并行】的【多个】流程任务的 assignee 负责人的分配
|
||||
* 第一步,基于分配规则,计算出分配任务的【多个】候选人们。
|
||||
* 第二步,将【多个】任务候选人们,设置到 DelegateExecution 的 collectionVariable 变量中,以便 BpmUserTaskActivityBehavior 使用它
|
||||
* 自定义的【串行】的【多个】流程任务的 assignee 负责人的分配
|
||||
*
|
||||
* @author kemengkai
|
||||
* @since 2022-04-21 16:57
|
||||
* 本质上,实现和 {@link BpmParallelMultiInstanceBehavior} 一样,只是继承的类不一样
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Setter
|
||||
public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior {
|
||||
public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceBehavior {
|
||||
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
public BpmParallelMultiInstanceBehavior(Activity activity,
|
||||
AbstractBpmnActivityBehavior innerActivityBehavior) {
|
||||
public BpmSequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) {
|
||||
super(activity, innerActivityBehavior);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写该方法,主要实现两个功能:
|
||||
* 1. 忽略原有的 collectionVariable、collectionElementVariable 表达式,而是采用自己定义的
|
||||
* 2. 获得任务的处理人,并设置到 collectionVariable 中,用于 BpmUserTaskActivityBehavior 从中可以获取任务的处理人
|
||||
* 逻辑和 {@link BpmParallelMultiInstanceBehavior#resolveNrOfInstances(DelegateExecution)} 类似
|
||||
*
|
||||
* 注意,多个任务实例,每个任务实例对应一个处理人,所以返回的数量就是任务处理人的数量
|
||||
*
|
||||
* @param execution 执行任务
|
||||
* @return 数量
|
||||
* 差异的点:是在【第二步】的时候,需要返回 LinkedHashSet 集合!因为它需要有序!
|
||||
*/
|
||||
@Override
|
||||
protected int resolveNrOfInstances(DelegateExecution execution) {
|
||||
@@ -58,8 +49,9 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
|
||||
super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
|
||||
|
||||
// 第二步,获取任务的所有处理人
|
||||
// 不使用 execution.getVariable 原因:目前依次审批任务回退后 collectionVariable 变量没有清理, 如果重新进入该任务不会重新分配审批人
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);
|
||||
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariableLocal(super.collectionVariable, Set.class);
|
||||
if (assigneeUserIds == null) {
|
||||
assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||
if (CollUtil.isEmpty(assigneeUserIds)) {
|
||||
@@ -88,4 +80,19 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
|
||||
return super.resolveNrOfInstances(execution);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void executeOriginalBehavior(DelegateExecution execution, ExecutionEntity multiInstanceRootExecution, int loopCounter) {
|
||||
// 参见 https://t.zsxq.com/53Meo 情况
|
||||
if (execution.getCurrentFlowElement() instanceof CallActivity
|
||||
|| execution.getCurrentFlowElement() instanceof SubProcess) {
|
||||
super.executeOriginalBehavior(execution, multiInstanceRootExecution, loopCounter);
|
||||
return;
|
||||
}
|
||||
// 参见 https://gitee.com/zhijiantianya/yudao-cloud/issues/IC239F
|
||||
super.collectionExpression = null;
|
||||
super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
|
||||
super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
|
||||
super.executeOriginalBehavior(execution, multiInstanceRootExecution, loopCounter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,15 +6,15 @@ 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.framework.common.util.spring.SpringUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
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.http.*;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
@@ -26,7 +26,7 @@ import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_CALL_ERROR;
|
||||
|
||||
/**
|
||||
* 工作流发起 HTTP 请求工具类
|
||||
@@ -42,7 +42,6 @@ public class BpmHttpRequestUtils {
|
||||
List<BpmSimpleModelNodeVO.HttpRequestParam> bodyParams,
|
||||
Boolean handleResponse,
|
||||
List<KeyValue<String, String>> response) {
|
||||
RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class);
|
||||
BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class);
|
||||
|
||||
// 1.1 设置请求头
|
||||
@@ -51,6 +50,7 @@ public class BpmHttpRequestUtils {
|
||||
MultiValueMap<String, String> body = buildHttpBody(processInstance, bodyParams);
|
||||
|
||||
// 2. 发起请求
|
||||
RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class);
|
||||
ResponseEntity<String> responseEntity = sendHttpRequest(url, headers, body, restTemplate);
|
||||
|
||||
// 3. 处理返回
|
||||
@@ -78,27 +78,55 @@ public class BpmHttpRequestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void executeBpmHttpRequest(BpmProcessInstanceStatusEvent event,
|
||||
String url) {
|
||||
// 1.1 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
if (TenantContextHolder.getTenantId() != null) {
|
||||
headers.add(HEADER_TENANT_ID, String.valueOf(TenantContextHolder.getTenantId()));
|
||||
} else {
|
||||
BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class);
|
||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getId());
|
||||
if (processInstance != null) {
|
||||
headers.add(HEADER_TENANT_ID, String.valueOf(TenantContextHolder.getTenantId()));
|
||||
}
|
||||
}
|
||||
// 1.2 设置请求体
|
||||
// MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
|
||||
// body.add("id", event.getId());
|
||||
// body.add("processDefinitionKey", event.getProcessDefinitionKey());
|
||||
// body.add("status", event.getStatus().toString());
|
||||
// if (StrUtil.isNotEmpty(event.getBusinessKey())) {
|
||||
// body.add("businessKey", event.getBusinessKey());
|
||||
// }
|
||||
|
||||
// 2. 发起请求
|
||||
RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class);
|
||||
sendHttpRequest(url, headers, event, restTemplate);
|
||||
}
|
||||
|
||||
public static ResponseEntity<String> sendHttpRequest(String url,
|
||||
MultiValueMap<String, String> headers,
|
||||
MultiValueMap<String, String> body,
|
||||
Object body,
|
||||
RestTemplate restTemplate) {
|
||||
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
|
||||
HttpEntity<Object> requestEntity = new HttpEntity<>(body, headers);
|
||||
ResponseEntity<String> responseEntity;
|
||||
try {
|
||||
responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
|
||||
log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity);
|
||||
log.info("[sendHttpRequest][HTTP 请求,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity);
|
||||
} catch (RestClientException e) {
|
||||
log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage());
|
||||
throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR);
|
||||
log.error("[sendHttpRequest][HTTP 请求,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage());
|
||||
throw exception(PROCESS_INSTANCE_HTTP_CALL_ERROR);
|
||||
}
|
||||
return responseEntity;
|
||||
}
|
||||
|
||||
public static MultiValueMap<String, String> buildHttpHeaders(ProcessInstance processInstance,
|
||||
List<BpmSimpleModelNodeVO.HttpRequestParam> headerSettings) {
|
||||
Map<String, Object> processVariables = processInstance.getProcessVariables();
|
||||
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
headers.add(HEADER_TENANT_ID, processInstance.getTenantId());
|
||||
Map<String, Object> processVariables = processInstance.getProcessVariables();
|
||||
addHttpRequestParam(headers, headerSettings, processVariables);
|
||||
return headers;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,6 @@ import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID;
|
||||
@@ -221,11 +220,6 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
List<ActivityNode> simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel,
|
||||
processDefinitionInfo,
|
||||
processVariables, activities);
|
||||
// 3.3 如果是发起动作,activityId 为开始节点,不校验审批人自选节点
|
||||
if (ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) {
|
||||
simulateActivityNodes.removeIf(node ->
|
||||
BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()));
|
||||
}
|
||||
|
||||
// 4. 拼接最终数据
|
||||
return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance,
|
||||
@@ -415,7 +409,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
endActivities.forEach(activity -> {
|
||||
// StartEvent:只处理 BPMN 的场景。因为,SIMPLE 情况下,已经有 START_USER_NODE 节点
|
||||
if (ELEMENT_EVENT_START.equals(activity.getActivityType())
|
||||
&& BpmModelTypeEnum.BPMN.getType().equals(processDefinitionInfo.getModelType())) {
|
||||
&& BpmModelTypeEnum.BPMN.getType().equals(processDefinitionInfo.getModelType())
|
||||
&& !CollUtil.contains(activities, // 特殊:如果已经存在用户手动创建的 START_USER_NODE_ID 节点,则忽略 StartEvent
|
||||
historicActivity -> historicActivity.getActivityId().equals(START_USER_NODE_ID))) {
|
||||
ActivityNodeTask startTask = new ActivityNodeTask().setId(BpmnModelConstants.START_USER_NODE_ID)
|
||||
.setAssignee(startUserId).setStatus(BpmTaskStatusEnum.APPROVE.getStatus());
|
||||
ActivityNode startNode = new ActivityNode().setId(startTask.getId())
|
||||
@@ -555,7 +551,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
// 情况一:BPMN 设计器
|
||||
if (Objects.equals(BpmModelTypeEnum.BPMN.getType(), processDefinitionInfo.getModelType())) {
|
||||
List<FlowElement> flowElements = BpmnModelUtils.simulateProcess(bpmnModel, processVariables);
|
||||
return convertList(flowElements, flowElement -> buildNotRunApproveNodeForBpmn(startUserId, bpmnModel,
|
||||
return convertList(flowElements, flowElement -> buildNotRunApproveNodeForBpmn(
|
||||
startUserId, bpmnModel, flowElements,
|
||||
processDefinitionInfo, processVariables, flowElement, runActivityIds));
|
||||
}
|
||||
// 情况二:SIMPLE 设计器
|
||||
@@ -563,7 +560,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
BpmSimpleModelNodeVO simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(),
|
||||
BpmSimpleModelNodeVO.class);
|
||||
List<BpmSimpleModelNodeVO> simpleNodes = SimpleModelUtils.simulateProcess(simpleModel, processVariables);
|
||||
return convertList(simpleNodes, simpleNode -> buildNotRunApproveNodeForSimple(startUserId, bpmnModel,
|
||||
return convertList(simpleNodes, simpleNode -> buildNotRunApproveNodeForSimple(
|
||||
startUserId, bpmnModel,
|
||||
processDefinitionInfo, processVariables, simpleNode, runActivityIds));
|
||||
}
|
||||
throw new IllegalArgumentException("未知设计器类型:" + processDefinitionInfo.getModelType());
|
||||
@@ -618,8 +616,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
return null;
|
||||
}
|
||||
|
||||
private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel,
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
|
||||
private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, List<FlowElement> flowElements,
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||
Map<String, Object> processVariables,
|
||||
FlowElement node, Set<String> runActivityIds) {
|
||||
if (runActivityIds.contains(node.getId())) {
|
||||
return null;
|
||||
@@ -634,6 +633,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
|
||||
// 1. 开始节点
|
||||
if (node instanceof StartEvent) {
|
||||
if (CollUtil.contains(flowElements, // 特殊:如果已经存在用户手动创建的 START_USER_NODE_ID 节点,则忽略 StartEvent
|
||||
flowElement -> flowElement.getId().equals(START_USER_NODE_ID))) {
|
||||
return null;
|
||||
}
|
||||
return activityNode.setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName())
|
||||
.setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType());
|
||||
}
|
||||
|
||||
@@ -8,9 +8,11 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCa
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryServiceImpl;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
|
||||
@@ -32,6 +34,9 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest {
|
||||
@Resource
|
||||
private BpmCategoryServiceImpl categoryService;
|
||||
|
||||
@MockitoBean
|
||||
private BpmModelService modelService;
|
||||
|
||||
@Resource
|
||||
private BpmCategoryMapper categoryMapper;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user