feat: 子流程
This commit is contained in:
parent
ceba5b8cec
commit
9698fee364
|
@ -30,6 +30,9 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable<Integer> {
|
|||
DELAY_TIMER_NODE(14, "延迟器", "receiveTask"),
|
||||
TRIGGER_NODE(15, "触发器", "serviceTask"),
|
||||
|
||||
CHILD_PROCESS(20, "子流程", "callActivity"),
|
||||
ASYNC_CHILD_PROCESS(21, "异步子流程", "callActivity"),
|
||||
|
||||
// 50 ~ 条件分支
|
||||
CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式
|
||||
CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"),
|
||||
|
|
|
@ -26,6 +26,7 @@ public enum BpmReasonEnum {
|
|||
TIMEOUT_REJECT("审批超时,系统自动不通过"),
|
||||
ASSIGN_START_USER_APPROVE("审批人与提交人为同一人时,自动通过"),
|
||||
ASSIGN_START_USER_APPROVE_WHEN_SKIP("审批人与提交人为同一人时,自动通过"),
|
||||
ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE("发起人节点首次自动通过"),
|
||||
ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND("审批人与提交人为同一人时,找不到部门负责人,自动通过"),
|
||||
ASSIGN_START_USER_TRANSFER_DEPT_LEADER("审批人与提交人为同一人时,转交给部门负责人审批"),
|
||||
ASSIGN_EMPTY_APPROVE("审批人为空,自动通过"),
|
||||
|
|
|
@ -10,6 +10,7 @@ import jakarta.validation.Valid;
|
|||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.flowable.bpmn.model.IOParameter;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -122,6 +123,8 @@ public class BpmSimpleModelNodeVO {
|
|||
*/
|
||||
private TriggerSetting triggerSetting;
|
||||
|
||||
private ChildProcessSetting childProcessSetting;
|
||||
|
||||
@Schema(description = "任务监听器")
|
||||
@Valid
|
||||
@Data
|
||||
|
@ -397,4 +400,33 @@ public class BpmSimpleModelNodeVO {
|
|||
private Map<String, Object> updateFormFields;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "子流程节点配置")
|
||||
@Data
|
||||
@Valid
|
||||
public static class ChildProcessSetting {
|
||||
|
||||
@Schema(description = "被调用流程", example = "xxx")
|
||||
@NotEmpty(message = "被调用流程不能为空")
|
||||
private String calledElement;
|
||||
|
||||
@Schema(description = "被调用流程名称", example = "xxx")
|
||||
@NotEmpty(message = "被调用流程名称不能为空")
|
||||
private String calledElementName;
|
||||
|
||||
@Schema(description = "是否异步", example = "false")
|
||||
@NotNull(message = "是否异步不能为空")
|
||||
private Boolean async;
|
||||
|
||||
@Schema(description = "输入参数(主->子)", example = "[]")
|
||||
private List<IOParameter> inVariable;
|
||||
|
||||
@Schema(description = "输出参数(子->主)", example = "[]")
|
||||
private List<IOParameter> outVariable;
|
||||
|
||||
@Schema(description = "是否自动跳过子流程发起节点", example = "false")
|
||||
@NotNull(message = "是否自动跳过子流程发起节点不能为空")
|
||||
private Boolean skipStartUserNode;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,13 @@ public class BpmnVariableConstants {
|
|||
*/
|
||||
public static final String PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED";
|
||||
|
||||
/**
|
||||
* 流程实例的变量 - 用于判断流程是否需要跳过发起人节点
|
||||
*
|
||||
* @see ProcessInstance#getProcessVariables()
|
||||
*/
|
||||
public static final String PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE = "PROCESS_SKIP_START_USER_NODE";
|
||||
|
||||
/**
|
||||
* 流程实例的变量 - 流程开始时间
|
||||
*
|
||||
|
|
|
@ -22,6 +22,8 @@ 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.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE;
|
||||
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS;
|
||||
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;
|
||||
|
@ -42,7 +44,8 @@ public class SimpleModelUtils {
|
|||
List<NodeConvert> converts = asList(new StartNodeConvert(), new EndNodeConvert(),
|
||||
new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(),
|
||||
new DelayTimerNodeConvert(), new TriggerNodeConvert(),
|
||||
new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert());
|
||||
new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert(),
|
||||
new ChildProcessConvert(), new AsyncChildProcessConvert());
|
||||
converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert));
|
||||
}
|
||||
|
||||
|
@ -773,6 +776,66 @@ public class SimpleModelUtils {
|
|||
|
||||
}
|
||||
|
||||
private static class ChildProcessConvert implements NodeConvert {
|
||||
|
||||
@Override
|
||||
public CallActivity convert(BpmSimpleModelNodeVO node) {
|
||||
BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting();
|
||||
List<IOParameter> inVariable = childProcessSetting.getInVariable() == null ?
|
||||
new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariable());
|
||||
CallActivity callActivity = new CallActivity();
|
||||
callActivity.setId(node.getId());
|
||||
callActivity.setName(node.getName());
|
||||
callActivity.setCalledElementType("key");
|
||||
// 1. 是否异步
|
||||
callActivity.setAsynchronous(node.getChildProcessSetting().getAsync());
|
||||
// 2. 调用的子流程
|
||||
callActivity.setCalledElement(childProcessSetting.getCalledElement());
|
||||
callActivity.setProcessInstanceName(childProcessSetting.getCalledElementName());
|
||||
// 3. 是否自动跳过子流程发起节点
|
||||
if (Boolean.TRUE.equals(childProcessSetting.getSkipStartUserNode())) {
|
||||
IOParameter ioParameter = new IOParameter();
|
||||
ioParameter.setSourceExpression("true");
|
||||
ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE);
|
||||
inVariable.add(ioParameter);
|
||||
} else {
|
||||
IOParameter ioParameter = new IOParameter();
|
||||
ioParameter.setSourceExpression("false");
|
||||
ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE);
|
||||
inVariable.add(ioParameter);
|
||||
}
|
||||
// 4. 主→子变量传递
|
||||
// 默认需要传递的一些变量
|
||||
// 4.1 流程状态
|
||||
IOParameter ioParameter = new IOParameter();
|
||||
ioParameter.setSource(PROCESS_INSTANCE_VARIABLE_STATUS);
|
||||
ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_STATUS);
|
||||
inVariable.add(ioParameter);
|
||||
callActivity.setInParameters(inVariable);
|
||||
// 5. 子→主变量传递
|
||||
if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) {
|
||||
callActivity.setOutParameters(childProcessSetting.getOutVariable());
|
||||
}
|
||||
return callActivity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmSimpleModelNodeTypeEnum getType() {
|
||||
return BpmSimpleModelNodeTypeEnum.CHILD_PROCESS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class AsyncChildProcessConvert extends ChildProcessConvert {
|
||||
|
||||
@Override
|
||||
public BpmSimpleModelNodeTypeEnum getType() {
|
||||
return BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static String buildGatewayJoinId(String id) {
|
||||
return id + "_join";
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cn.iocoder.yudao.module.bpm.service.task;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
|
@ -63,6 +64,7 @@ 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.BpmnModelConstants.START_USER_NODE_ID;
|
||||
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.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE;
|
||||
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*;
|
||||
|
||||
/**
|
||||
|
@ -1212,19 +1214,30 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
}
|
||||
}
|
||||
|
||||
// 审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理
|
||||
if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
|
||||
// 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略
|
||||
// TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识
|
||||
Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(),
|
||||
String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class);
|
||||
if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
|
||||
// 发起人节点
|
||||
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
|
||||
if (bpmnModel == null) {
|
||||
log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId());
|
||||
return;
|
||||
}
|
||||
FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
|
||||
// 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略
|
||||
// TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识
|
||||
Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(),
|
||||
String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class);
|
||||
Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(),
|
||||
PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class));
|
||||
if (userTaskElement.getId().equals(START_USER_NODE_ID) &&
|
||||
(skipStartUserNodeFlag == null || Boolean.TRUE.equals(skipStartUserNodeFlag)) &&
|
||||
!Boolean.TRUE.equals(returnTaskFlag)) {
|
||||
getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
|
||||
.setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason()));
|
||||
return;
|
||||
}
|
||||
// 当不为发起人节点时,审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理
|
||||
if (!userTaskElement.getId().equals(START_USER_NODE_ID) &&
|
||||
StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
|
||||
if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
|
||||
Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement);
|
||||
|
||||
// 情况一:自动跳过
|
||||
|
|
Loading…
Reference in New Issue