feat(REQ-4418) - 添加推测未来节点日志时的抄送节点配置内容

This commit is contained in:
wangli 2025-08-25 16:26:11 +08:00
parent aa7f8cb854
commit 1aecc6dac3
5 changed files with 81 additions and 38 deletions

View File

@ -13,7 +13,6 @@ import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.CustomProperty;
import org.flowable.bpmn.model.ServiceTask;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
@ -41,6 +40,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE;
import static cn.axzo.workflow.common.constant.BpmnConstants.OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverSpecify;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.getLimitedElementList;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.removeDuplicateByPersonId;
@ -60,18 +60,20 @@ public class CustomCarbonCopyUserSelectorCmd extends AbstractCommand<List<BpmnTa
private final EngineExecutionStartListener engineExecutionStartListener;
private final BpmnHistoricTaskInstanceConverter historicTaskInstanceConverter;
private final String serviceVersion;
private final Integer categoryVersion;
/**
* 模型中发起节点立马接着抄送节点,此时无法通过 runtimeService 得到正运行的 execution,所以由外部传递
*/
private DelegateExecution execution;
public CustomCarbonCopyUserSelectorCmd(String processInstanceId, List<BpmnCarbonCopyConf> carbons, ServiceTask serviceTask, EngineExecutionStartListener engineExecutionStartListener, BpmnHistoricTaskInstanceConverter historicTaskInstanceConverter, String serviceVersion) {
public CustomCarbonCopyUserSelectorCmd(String processInstanceId, List<BpmnCarbonCopyConf> carbons, ServiceTask serviceTask, EngineExecutionStartListener engineExecutionStartListener, BpmnHistoricTaskInstanceConverter historicTaskInstanceConverter, String serviceVersion, Integer categoryVersion) {
this.processInstanceId = processInstanceId;
this.carbons = carbons;
this.serviceTask = serviceTask;
this.engineExecutionStartListener = engineExecutionStartListener;
this.historicTaskInstanceConverter = historicTaskInstanceConverter;
this.serviceVersion = serviceVersion;
this.categoryVersion = categoryVersion;
}
public CustomCarbonCopyUserSelectorCmd(String processInstanceId, List<BpmnCarbonCopyConf> carbons,
@ -79,6 +81,7 @@ public class CustomCarbonCopyUserSelectorCmd extends AbstractCommand<List<BpmnTa
EngineExecutionStartListener engineExecutionStartListener,
BpmnHistoricTaskInstanceConverter historicTaskInstanceConverter,
String serviceVersion,
Integer categoryVersion,
DelegateExecution execution) {
this.processInstanceId = processInstanceId;
this.carbons = carbons;
@ -86,6 +89,7 @@ public class CustomCarbonCopyUserSelectorCmd extends AbstractCommand<List<BpmnTa
this.engineExecutionStartListener = engineExecutionStartListener;
this.historicTaskInstanceConverter = historicTaskInstanceConverter;
this.serviceVersion = serviceVersion;
this.categoryVersion = categoryVersion;
this.execution = execution;
}
@ -100,6 +104,36 @@ public class CustomCarbonCopyUserSelectorCmd extends AbstractCommand<List<BpmnTa
@Override
public List<BpmnTaskDelegateAssigner> execute(CommandContext commandContext) {
List<BpmnTaskDelegateAssigner> assigners = new ArrayList<>();
if (Objects.isNull(categoryVersion) || categoryVersion < 2) {
assigners.addAll(calcUserV1(commandContext));
} else {
// v2 版本
assigners.addAll(calcUserV2(commandContext));
}
return getLimitedElementList(removeDuplicateByPersonId(assigners), CARBON_ASSIGNER_LIMIT_NUMBER);
}
private List<BpmnTaskDelegateAssigner> calcUserV2(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
List<Execution> executions = new ArrayList<>();
if (Objects.isNull(execution)) {
executions.addAll(runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list());
} else {
executions.add((Execution) execution);
}
List<BpmnTaskDelegateAssigner> assigners = new ArrayList<>();
getApproverSpecify(serviceTask).ifPresent(specify -> {
assigners.addAll(engineExecutionStartListener.approverSelect(specify.getType(), serviceTask,
(DelegateExecution) executions.get(0), false));
});
return assigners;
}
private List<BpmnTaskDelegateAssigner> calcUserV1(CommandContext commandContext) {
if (CollectionUtils.isEmpty(carbons)) {
return Collections.emptyList();
}
@ -159,7 +193,7 @@ public class CustomCarbonCopyUserSelectorCmd extends AbstractCommand<List<BpmnTa
break;
}
});
return getLimitedElementList(removeDuplicateByPersonId(assigners), CARBON_ASSIGNER_LIMIT_NUMBER);
return assigners;
}
private List<BpmnTaskDelegateAssigner> getApproverRelationUser(BpmnCarbonCopyConf carbon,

View File

@ -38,6 +38,7 @@ import java.util.concurrent.atomic.AtomicReference;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getActivitySignature;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getCarbonCopyConfigs;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getCategoryVersion;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getNoticeConfig;
import static cn.axzo.workflow.core.listener.AbstractBpmnEventListener.parseProcessDefinitionKey;
@ -74,14 +75,14 @@ public class EngineCarbonCopyEventListener implements JavaDelegate {
String processInstanceId = execution.getProcessInstanceId();
CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor();
Process mainProcess = ProcessDefinitionUtil.getBpmnModel(processDefinitionId).getMainProcess();
Integer categoryVersion = getCategoryVersion(mainProcess).orElse(0);
ServiceTask serviceTask = (ServiceTask) mainProcess.getFlowElement(currentActivityId);
List<BpmnTaskDelegateAssigner> carbonUsers = new ArrayList<>();
getCarbonCopyConfigs(serviceTask).ifPresent(carbons -> {
carbonUsers.addAll(commandExecutor.execute(new CustomCarbonCopyUserSelectorCmd(processInstanceId,
carbons, serviceTask, engineExecutionStartListener,
historicTaskInstanceConverter, serviceVersion, execution)));
historicTaskInstanceConverter, serviceVersion, categoryVersion, execution)));
});
RuntimeService runtimeService =

View File

@ -221,6 +221,7 @@ import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApprove
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverSpecify;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getButtonConfig;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getCarbonCopyConfigs;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getCategoryVersion;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getExcludeCooperateShipTypes;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getExcludeIdentityTypes;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getInitiatorSpecifiedFilter;
@ -1050,6 +1051,7 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
String startNodeDefinitionKey,
Boolean containSelf) {
BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
Integer categoryVersion = getCategoryVersion(bpmnModel.getMainProcess()).orElse(1);
List<ProcessNodeDetailVO> resultList = new ArrayList<>(flowElements.size());
// 发起人节点,也是一个 UserTask 节点, 所以这里默认就包含了发起人节点
flowElements.stream()
@ -1073,7 +1075,7 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
if (i instanceof UserTask) {
parseUserTask(processInstanceId, (UserTask) i, node, nodeDefinitionKeys);
} else if (i instanceof ServiceTask) {
parseServiceTask(processInstanceId, (ServiceTask) i, node, nodeDefinitionKeys);
parseServiceTask(processInstanceId, (ServiceTask) i, node, nodeDefinitionKeys, categoryVersion);
}
resultList.add(node);
});
@ -1081,17 +1083,23 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
}
private void parseServiceTask(String processInstanceId, ServiceTask i, ProcessNodeDetailVO
node, List<String> skipTaskDefinitionKeys) {
node, List<String> skipTaskDefinitionKeys, Integer categoryVersion) {
// ServiceTask 主要作用于抄送
node.setId(i.getId()).setName(i.getName());
if (skipTaskDefinitionKeys.contains(i.getId())) {
return;
}
getCarbonCopyConfigs(i).ifPresent(carbons ->
node.setForecastAssigners(springProcessEngineConfiguration.getCommandExecutor()
.execute(new CustomCarbonCopyUserSelectorCmd(processInstanceId, carbons,
i, engineExecutionStartListener,
historicTaskInstanceConverter, serviceVersion))));
if (categoryVersion < 2) {
getCarbonCopyConfigs(i).ifPresent(carbons ->
node.setForecastAssigners(springProcessEngineConfiguration.getCommandExecutor()
.execute(new CustomCarbonCopyUserSelectorCmd(processInstanceId, carbons,
i, engineExecutionStartListener,
historicTaskInstanceConverter, serviceVersion, categoryVersion))));
} else {
getApproverSpecify(i).ifPresent(specify -> {
node.setForecastAssigners(engineExecutionStartListener.approverSelect(specify.getType(), i, null, false));
});
}
}
private void parseUserTask(String processInstanceId, UserTask i, ProcessNodeDetailVO

View File

@ -91,13 +91,13 @@ public class BasedRoleV2TaskAssigneeSelector extends AbstractBpmnTaskAssigneeSel
break;
case within_the_project_construction_units:
log.info("executing role v2 with within_the_project_construction_units");
CooperateShipTypeEnum workflowType = getCooperateShipType(flowElement).orElseThrow(() -> new WorkflowEngineException(ENGINE_ROLE_V2_CONFIG_INVALID, "缺少参建单位类型配置", flowElement.getId()));
CooperateShipTypeEnum cooperateShipTypeEnum = getCooperateShipType(flowElement).orElseThrow(() -> new WorkflowEngineException(ENGINE_ROLE_V2_CONFIG_INVALID, "缺少参建单位类型配置", flowElement.getId()));
v2ReqBuilder.scope(FlowTaskAssignerScopeDTO.builder()
.nodeId(orgDTO.getNodeId())
.upLevel(SignApproverOrgLimitEnum.LV_ALL.getCode())
.crossDomain(true)
.cooperateTypes(Sets.newHashSet(cn.axzo.orgmanax.dto.cooperateship.enums.CooperateShipTypeEnum.valueOf(workflowType.name())))
.cooperateTypes(Sets.newHashSet(cn.axzo.orgmanax.dto.cooperateship.enums.CooperateShipTypeEnum.valueOf(cooperateShipTypeEnum.name())))
.build());
break;
case specified_org:

View File

@ -89,7 +89,7 @@
<jsonValue><![CDATA[{"id":"NODE_STARTER","type":"NODE_STARTER","name":"发起人","property":{"isMultiTask":true,"isSequential":false,"groupsType":"or","fieldPermission":[]}}]]></jsonValue>
</extensionElements>
</userTask>
<sequenceFlow id="SequenceFlowId_ebf6747caaa541cf889d61c08f25dd92" name="发起人" sourceRef="startEventNode"
<sequenceFlow id="SequenceFlowId_26bf7f7a4d624658964562d4c5b1f17d" name="发起人" sourceRef="startEventNode"
targetRef="NODE_STARTER"></sequenceFlow>
<userTask id="node_864990574287_ylc5" name="审批节点" flowable:assignee="${assigneeName}"
flowable:formKey="test-form">
@ -181,7 +181,7 @@
<completionCondition>${nrOfInstances != nrOfActiveInstances}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
<sequenceFlow id="SequenceFlowId_2e6c51a28f654e089b05dea623b849aa" name="审批节点" sourceRef="NODE_STARTER"
<sequenceFlow id="SequenceFlowId_a0fb39e606584def84e4e186a4803d6a" name="审批节点" sourceRef="NODE_STARTER"
targetRef="node_864990574287_ylc5"></sequenceFlow>
<serviceTask id="node_865038288523_ff8k" name="抄送节点"
flowable:delegateExpression="${engineCarbonCopyV2EventListener}">
@ -200,7 +200,7 @@
<![CDATA[{"id":"node_865038288523_ff8k","parentId":"node_864990574287_ylc5","type":"NODE_CARBON_COPY","name":"抄送节点","property":{"approverSpecify":"role_v2","approverSpecifyRange":"within_the_project_construction_units","cooperateShipType":"PROJ_PRIMARY_CONTRACTING_UNIT","approverSpecifyRangeUnit":"in_ent","initiatorSpecifiedFilter":false,"excludeIdentityTypes":[],"excludeCooperateShipTypes":[],"specifyValue":"[{\"name\":\"其他\",\"value\":101357,\"type\":7},{\"name\":\"首页-待删除\",\"value\":101289,\"type\":7},{\"name\":\"班组管理(劳资)\",\"value\":101290,\"type\":7},{\"name\":\"人员管理-T\",\"value\":101291,\"type\":7},{\"name\":\"查看企业通讯录\",\"value\":101292,\"type\":7},{\"name\":\"部门管理-T\",\"value\":101293,\"type\":7},{\"name\":\"班组管理\",\"value\":101294,\"type\":7},{\"name\":\"岗位权限管理-T\",\"value\":101295,\"type\":7},{\"name\":\"编辑工程-待删除\",\"value\":101296,\"type\":7},{\"name\":\"查看工程\",\"value\":101297,\"type\":7},{\"name\":\"申请工程完结-待删除\",\"value\":101356,\"type\":7},{\"name\":\"发薪账户管理\",\"value\":101298,\"type\":7},{\"name\":\"查看发薪报表-T\",\"value\":101299,\"type\":7},{\"name\":\"招工\",\"value\":101300,\"type\":7}]","isMultiTask":true,"isSequential":false,"groupsType":"or","carbonCopyConf":[],"fieldPermission":[]}}]]></jsonValue>
</extensionElements>
</serviceTask>
<sequenceFlow id="SequenceFlowId_2e5686e25977452695e77d9bc987f406" name="抄送节点"
<sequenceFlow id="SequenceFlowId_39fe8ba883b34f5a89fbe71774c099d7" name="抄送节点"
sourceRef="node_864990574287_ylc5" targetRef="node_865038288523_ff8k"></sequenceFlow>
<userTask id="node_865016003134_707l" name="业务节点" flowable:assignee="${assigneeName}"
flowable:formKey="test-form">
@ -293,9 +293,9 @@
<completionCondition>${nrOfInstances != nrOfActiveInstances}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
<sequenceFlow id="SequenceFlowId_9d31691309714dee93441f2426150027" name="业务节点"
<sequenceFlow id="SequenceFlowId_75cf225cb6104b5caa27f11ae1479822" name="业务节点"
sourceRef="node_865038288523_ff8k" targetRef="node_865016003134_707l"></sequenceFlow>
<sequenceFlow id="SequenceFlowId_52b0f93d272d44fb981f18a17ea04ea8" sourceRef="node_865016003134_707l"
<sequenceFlow id="SequenceFlowId_dd624c7933bc40a2ae852088c52d8f4e" sourceRef="node_865016003134_707l"
targetRef="endEventNode"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_id">
@ -318,40 +318,40 @@
<bpmndi:BPMNShape bpmnElement="NODE_STARTER" id="BPMNShape_NODE_STARTER">
<omgdc:Bounds height="60.0" width="100.0" x="80.0" y="0.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="SequenceFlowId_9d31691309714dee93441f2426150027"
id="BPMNEdge_SequenceFlowId_9d31691309714dee93441f2426150027">
<omgdi:waypoint x="480.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="492.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="492.0" y="30.000000000000007"></omgdi:waypoint>
<omgdi:waypoint x="530.0" y="30.000000000000007"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="SequenceFlowId_52b0f93d272d44fb981f18a17ea04ea8"
id="BPMNEdge_SequenceFlowId_52b0f93d272d44fb981f18a17ea04ea8">
<bpmndi:BPMNEdge bpmnElement="SequenceFlowId_dd624c7933bc40a2ae852088c52d8f4e"
id="BPMNEdge_SequenceFlowId_dd624c7933bc40a2ae852088c52d8f4e">
<omgdi:waypoint x="630.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="642.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="642.0" y="30.000000000000004"></omgdi:waypoint>
<omgdi:waypoint x="680.0" y="30.000000000000004"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="SequenceFlowId_2e6c51a28f654e089b05dea623b849aa"
id="BPMNEdge_SequenceFlowId_2e6c51a28f654e089b05dea623b849aa">
<omgdi:waypoint x="180.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="192.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="192.0" y="30.000000000000007"></omgdi:waypoint>
<omgdi:waypoint x="230.0" y="30.000000000000007"></omgdi:waypoint>
<bpmndi:BPMNEdge bpmnElement="SequenceFlowId_75cf225cb6104b5caa27f11ae1479822"
id="BPMNEdge_SequenceFlowId_75cf225cb6104b5caa27f11ae1479822">
<omgdi:waypoint x="480.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="492.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="492.0" y="30.000000000000007"></omgdi:waypoint>
<omgdi:waypoint x="530.0" y="30.000000000000007"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="SequenceFlowId_ebf6747caaa541cf889d61c08f25dd92"
id="BPMNEdge_SequenceFlowId_ebf6747caaa541cf889d61c08f25dd92">
<bpmndi:BPMNEdge bpmnElement="SequenceFlowId_26bf7f7a4d624658964562d4c5b1f17d"
id="BPMNEdge_SequenceFlowId_26bf7f7a4d624658964562d4c5b1f17d">
<omgdi:waypoint x="30.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="42.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="42.0" y="30.000000000000007"></omgdi:waypoint>
<omgdi:waypoint x="80.0" y="30.000000000000007"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="SequenceFlowId_2e5686e25977452695e77d9bc987f406"
id="BPMNEdge_SequenceFlowId_2e5686e25977452695e77d9bc987f406">
<bpmndi:BPMNEdge bpmnElement="SequenceFlowId_39fe8ba883b34f5a89fbe71774c099d7"
id="BPMNEdge_SequenceFlowId_39fe8ba883b34f5a89fbe71774c099d7">
<omgdi:waypoint x="330.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="342.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="342.0" y="30.000000000000007"></omgdi:waypoint>
<omgdi:waypoint x="380.0" y="30.000000000000007"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="SequenceFlowId_a0fb39e606584def84e4e186a4803d6a"
id="BPMNEdge_SequenceFlowId_a0fb39e606584def84e4e186a4803d6a">
<omgdi:waypoint x="180.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="192.0" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="192.0" y="30.000000000000007"></omgdi:waypoint>
<omgdi:waypoint x="230.0" y="30.000000000000007"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>