Merge branch 'feature/countersign_ext' into dev

This commit is contained in:
wangli 2025-10-20 14:39:49 +08:00
commit a1e65b73f6
4 changed files with 140 additions and 68 deletions

View File

@ -47,7 +47,7 @@ public interface BpmnConstants {
String BIZ_NODE_ALTER = "[_BIZ_NODE_ALTER_]";
String INITIATOR_SPECIFY = "[_INITIATOR_SPECIFY_]";
String SIGNATURE_COLLECTION = "[_SIGNATURE_COLLECTION_]";
String FORWARD_COUNTERSIGN_COUNT = "[_FORWARD_COUNTERSIGN_COUNT_]";
String COUNTERSIGN_COUNT = "[_COUNTERSIGN_COUNT_]";
String PROCESS_PREFIX = "Flowable";
@Deprecated
String OLD_TASK_ASSIGNEE_SKIP_FLAT = "taskSkip";
@ -269,4 +269,8 @@ public interface BpmnConstants {
* 前加签节点 ID 片段
*/
String FORWARD_ACTIVITY_FRAGMENT = "[forward_sign]";
/**
* 后加签节点 ID 片段
*/
String BACK_ACTIVITY_FRAGMENT = "[back_sign]";
}

View File

@ -43,9 +43,10 @@ import java.util.stream.Collectors;
import static cn.axzo.workflow.common.code.OtherRespCode.ASSIGNEE_NODE_ID_NOT_EXISTS;
import static cn.axzo.workflow.common.constant.BpmnConstants.AND_SIGN_EXPRESSION;
import static cn.axzo.workflow.common.constant.BpmnConstants.BACK_ACTIVITY_FRAGMENT;
import static cn.axzo.workflow.common.constant.BpmnConstants.COUNTERSIGN_ASSIGNER_SHOW_NUMBER;
import static cn.axzo.workflow.common.constant.BpmnConstants.COUNTERSIGN_COUNT;
import static cn.axzo.workflow.common.constant.BpmnConstants.FORWARD_ACTIVITY_FRAGMENT;
import static cn.axzo.workflow.common.constant.BpmnConstants.FORWARD_COUNTERSIGN_COUNT;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.enums.BpmnCountersignTypeEnum.FORWARD_COUNTERSIGN;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.COUNTERSIGN;
@ -150,11 +151,11 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand<Void> implemen
switch (countersignType) {
case FORWARD_COUNTERSIGN:
// 加签的一种方式前加签具体定义由后续产品需求来定
forwardCountSign(commandContext, task, valuTargetAssigneeList);
forwardAndBackCountSign(commandContext, task, valuTargetAssigneeList);
break;
case BACK_COUNTERSIGN:
// 加签的另一种方式
backCountSign(commandContext, historicTaskInstance, task, valuTargetAssigneeList);
forwardAndBackCountSign(commandContext, task, valuTargetAssigneeList);
break;
default:
// 共享签不区分顺序
@ -166,13 +167,15 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand<Void> implemen
}
/**
* 前加签
* 后加签
* <p>
* 使用内存动态变更模型连接实现前加签功能
*
* @param commandContext
* @param task
* @param valuTargetAssigneeList
*/
private void forwardCountSign(CommandContext commandContext, TaskEntity task, List<BpmnTaskDelegateAssigner> valuTargetAssigneeList) {
private void forwardAndBackCountSign(CommandContext commandContext, TaskEntity task, List<BpmnTaskDelegateAssigner> valuTargetAssigneeList) {
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
@ -182,51 +185,90 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand<Void> implemen
UserTask originalUserTask = (UserTask) process.getFlowElement(task.getTaskDefinitionKey());
// 获取当前实例前加签次数
Long forwardCounterSignCount = runtimeService.getVariable(processInstance.getId(), FORWARD_COUNTERSIGN_COUNT, Long.class);
if (Objects.isNull(forwardCounterSignCount)) {
forwardCounterSignCount = 0L;
Long counterSignCount = runtimeService.getVariable(processInstance.getId(), COUNTERSIGN_COUNT, Long.class);
if (Objects.isNull(counterSignCount)) {
counterSignCount = 0L;
} else {
forwardCounterSignCount = forwardCounterSignCount + 1;
counterSignCount = counterSignCount + 1;
}
runtimeService.setVariable(processInstance.getId(), COUNTERSIGN_COUNT, counterSignCount);
BpmnFlowNodeMode nodeMode = Objects.equals(originalUserTask.getLoopCharacteristics().getCompletionCondition(), AND_SIGN_EXPRESSION) ? BpmnFlowNodeMode.AND : BpmnFlowNodeMode.OR;
// 创建前加签节点
// 生成加签节点ID
String newActivityId = originalUserTask.getId();
if (newActivityId.contains(FORWARD_ACTIVITY_FRAGMENT)) {
newActivityId = newActivityId.substring(0, newActivityId.indexOf(FORWARD_ACTIVITY_FRAGMENT));
switch (countersignType) {
case FORWARD_COUNTERSIGN:
newActivityId = processActivityId(newActivityId, FORWARD_ACTIVITY_FRAGMENT, counterSignCount);
break;
case BACK_COUNTERSIGN:
newActivityId = processActivityId(newActivityId, BACK_ACTIVITY_FRAGMENT, counterSignCount);
default:
break;
}
UserTask newUserTask = CustomBpmnModelHelper.createUserTask(processEngineConfiguration, originalUserTask, newActivityId + FORWARD_ACTIVITY_FRAGMENT + forwardCounterSignCount, nodeMode, valuTargetAssigneeList);
// 创建被加签节点
UserTask newUserTask = CustomBpmnModelHelper.createUserTask(processEngineConfiguration, originalUserTask, newActivityId, nodeMode, valuTargetAssigneeList);
// 加入模型
process.addFlowElement(newUserTask);
// 重新连接顺序流 (Sequence Flow)
CustomBpmnModelHelper.rewireSequenceFlows(process, originalUserTask, newUserTask);
CustomBpmnModelHelper.rewireSequenceFlows(process, originalUserTask, newUserTask, countersignType);
saveCounterSignRecord(valuTargetAssigneeList, processInstance, originalUserTask, newUserTask, nodeMode);
if (Objects.equals(FORWARD_COUNTERSIGN, countersignType)) {
log.info("前加签任务处理完成原任务ID:{},新任务ID:{}", task.getId(), newUserTask.getId());
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveActivityIdTo(task.getTaskDefinitionKey(), newUserTask.getId())
.changeState();
} else {
}
}
/**
* 前后加签动作记录用于 JVM 重启后的动态恢复
*
* @param valuTargetAssigneeList
* @param processInstance
* @param originalUserTask
* @param newUserTask
* @param nodeMode
*/
private void saveCounterSignRecord(List<BpmnTaskDelegateAssigner> valuTargetAssigneeList, ProcessInstance processInstance, UserTask originalUserTask, UserTask newUserTask, BpmnFlowNodeMode nodeMode) {
ExtAxDynamicSignRecord entity = new ExtAxDynamicSignRecord();
entity.setProcessInstanceId(processInstance.getProcessInstanceId());
entity.setProcessDefinitionId(processInstance.getProcessDefinitionId());
entity.setOriginalActivityId(originalUserTask.getId());
entity.setTargetActivityId(newUserTask.getId());
entity.setCounterSignType(FORWARD_COUNTERSIGN.getType());
entity.setCounterSignType(countersignType.getType());
entity.setTargetNodeMode(nodeMode.getType());
entity.setAssignerList(valuTargetAssigneeList);
extAxDynamicSignRecordService.saveOrUpdate(entity);
runtimeService.setVariable(processInstance.getId(), FORWARD_COUNTERSIGN_COUNT, forwardCounterSignCount);
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveActivityIdTo(task.getTaskDefinitionKey(), newUserTask.getId())
.changeState();
}
// 提取公共处理方法
private String processActivityId(String originalId, String fragment, long count) {
if (originalId.contains(fragment)) {
int fragmentIndex = originalId.indexOf(fragment);
return originalId.substring(0, fragmentIndex) + fragment + count;
}
return originalId; // 不包含目标片段时返回原字符串
}
/**
* 后加签
* <p>
* 基于当前节点的后加签内部实现主要依靠加减签的能力实现
*
* @param commandContext
* @param historicTaskInstance
* @param task
* @param valuTargetAssigneeList
*/
@Deprecated
private void backCountSign(CommandContext commandContext,
HistoricTaskInstance historicTaskInstance,
TaskEntity task,

View File

@ -3,6 +3,7 @@ package cn.axzo.workflow.core.engine.cmd.helper;
import cn.axzo.workflow.common.enums.ApprovalMethodEnum;
import cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum;
import cn.axzo.workflow.common.enums.ApproverSpecifyEnum;
import cn.axzo.workflow.common.enums.BpmnCountersignTypeEnum;
import cn.axzo.workflow.common.enums.BpmnFlowNodeMode;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
@ -69,7 +70,7 @@ public class CustomBpmnModelHelper {
newUserTask.setId(originalUserTask.getId());
}
newUserTask.setName(originalUserTask.getName());
newUserTask.setDocumentation("" + originalUserTask.getId() + "节点加签生成");
newUserTask.setDocumentation("" + originalUserTask.getId() + "节点加签生成");
MultiInstanceLoopCharacteristics loopCharacteristics = new MultiInstanceLoopCharacteristics();
loopCharacteristics.setInputDataItem(INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO + newUserTask.getId());
@ -129,7 +130,7 @@ public class CustomBpmnModelHelper {
nodeTypeElement.setElementText(BpmnFlowNodeType.NODE_TASK.getType());
newUserTask.addExtensionElement(nodeTypeElement);
// TODO 追加设置审批人为空的配置因为查找审批人模式是固定人员会执行退场离职校验导致最终审批人可能为空
// 追加设置审批人为空的配置因为查找审批人模式是固定人员会执行退场离职校验导致最终审批人可能为空
// 审批人为空时
ExtensionElement approverEmptyHandleTypeElement = new ExtensionElement();
approverEmptyHandleTypeElement.setName(CONFIG_APPROVER_EMPTY_HANDLE_TYPE);
@ -147,7 +148,6 @@ public class CustomBpmnModelHelper {
ActivityBehaviorFactory activityBehaviorFactory = processEngineConfiguration.getActivityBehaviorFactory();
UserTaskActivityBehavior userTaskActivityBehavior = activityBehaviorFactory.createUserTaskActivityBehavior(newUserTask);
// TODO 这里设置的 userTaskActivityBehavior 似乎是不对的需要
ParallelMultiInstanceBehavior behavior = activityBehaviorFactory.createParallelMultiInstanceBehavior(newUserTask, userTaskActivityBehavior);
behavior.setCollectionVariable(INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO + newUserTask.getId());
@ -157,31 +157,64 @@ public class CustomBpmnModelHelper {
return newUserTask;
}
public static void rewireSequenceFlows(Process process, UserTask originalUserTask, UserTask newUserTask) {
// 1. 找到所有指向原始节点的输入流
List<SequenceFlow> incomingFlows = originalUserTask.getIncomingFlows();
if (incomingFlows.isEmpty()) {
throw new WorkflowEngineException(CREATE_BPMN_PRE_SIGN_ERROR, "节点 " + originalUserTask.getId() + " 没有输入流,无法进行前加签");
public static void rewireSequenceFlows(Process process, UserTask originalUserTask, UserTask targetUserTask, BpmnCountersignTypeEnum countersignType) {
switch (countersignType) {
case FORWARD_COUNTERSIGN:
// 1. 找到所有指向原始节点的输入流
List<SequenceFlow> incomingFlows = originalUserTask.getIncomingFlows();
if (incomingFlows.isEmpty()) {
throw new WorkflowEngineException(CREATE_BPMN_PRE_SIGN_ERROR, "节点 " + originalUserTask.getId() + " 没有输入流,无法进行前加签");
}
// 2. 将这些输入流的目标从 originalUserTask 修改为 targetUserTask
for (SequenceFlow incomingFlow : incomingFlows) {
incomingFlow.setTargetRef(targetUserTask.getId());
// 如果需要也可以更新FlowElement中的引用但通常改TargetRef即可
}
targetUserTask.setIncomingFlows(incomingFlows);
// 3. 创建一个新的顺序流 targetUserTask 指向 originalUserTask
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId(id(SEQUENCE_FLOW_ID + "_ForwardSign"));
newSequenceFlow.setSourceRef(targetUserTask.getId());
newSequenceFlow.setSourceFlowElement(targetUserTask);
newSequenceFlow.setTargetRef(originalUserTask.getId());
newSequenceFlow.setTargetFlowElement(originalUserTask);
targetUserTask.setOutgoingFlows(Collections.singletonList(newSequenceFlow));
process.addFlowElement(newSequenceFlow);
originalUserTask.setIncomingFlows(Collections.singletonList(newSequenceFlow));
break;
case BACK_COUNTERSIGN:
// 1. 找到所有指向原始节点的输出流
List<SequenceFlow> outgoingFlows = originalUserTask.getOutgoingFlows();
if (outgoingFlows.isEmpty()) {
throw new WorkflowEngineException(CREATE_BPMN_PRE_SIGN_ERROR, "节点 " + originalUserTask.getId() + " 没有输出流,无法进行后加签");
}
// 2. 将这些输出流的源从 originalUserTask 修改为 targetUserTask
for (SequenceFlow outgoingFlow : outgoingFlows) {
outgoingFlow.setSourceRef(targetUserTask.getId());
// 如果需要也可以更新FlowElement中的引用但通常改SourceRef即可
}
targetUserTask.setOutgoingFlows(outgoingFlows);
// 3. 创建一个新的顺序流 originalUserTask 指向 targetUserTask
SequenceFlow backSequenceFlow = new SequenceFlow();
backSequenceFlow.setId(id(SEQUENCE_FLOW_ID + "_BackSign"));
backSequenceFlow.setSourceRef(originalUserTask.getId());
backSequenceFlow.setSourceFlowElement(originalUserTask);
backSequenceFlow.setTargetRef(targetUserTask.getId());
backSequenceFlow.setTargetFlowElement(targetUserTask);
originalUserTask.setOutgoingFlows(Collections.singletonList(backSequenceFlow));
process.addFlowElement(backSequenceFlow);
targetUserTask.setIncomingFlows(Collections.singletonList(backSequenceFlow));
break;
default:
break;
}
// 2. 将这些输入流的目标从 originalUserTask 修改为 newUserTask
for (SequenceFlow incomingFlow : incomingFlows) {
incomingFlow.setTargetRef(newUserTask.getId());
// 如果需要也可以更新FlowElement中的引用但通常改TargetRef即可
}
newUserTask.setIncomingFlows(incomingFlows);
// 3. 创建一个新的顺序流 newUserTask 指向 originalUserTask
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId(id(SEQUENCE_FLOW_ID + "_PreSign"));
newSequenceFlow.setSourceRef(newUserTask.getId());
newSequenceFlow.setSourceFlowElement(newUserTask);
newSequenceFlow.setTargetRef(originalUserTask.getId());
newSequenceFlow.setTargetFlowElement(originalUserTask);
newUserTask.setOutgoingFlows(Collections.singletonList(newSequenceFlow));
process.addFlowElement(newSequenceFlow);
originalUserTask.setIncomingFlows(Collections.singletonList(newSequenceFlow));
}
}

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.server.initializer;
import cn.axzo.workflow.common.enums.BpmnCountersignTypeEnum;
import cn.axzo.workflow.common.enums.BpmnFlowNodeMode;
import cn.axzo.workflow.core.engine.cmd.helper.CustomBpmnModelHelper;
import cn.axzo.workflow.core.repository.entity.ExtAxDynamicSignRecord;
@ -7,7 +8,6 @@ import cn.axzo.workflow.core.service.ExtAxDynamicSignRecordService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.impl.persistence.deploy.DeploymentManager;
import org.flowable.engine.repository.ProcessDefinition;
@ -26,8 +26,6 @@ import java.util.Set;
import java.util.stream.Collectors;
import static cn.axzo.workflow.common.constant.BpmnConstants.AND_SIGN_EXPRESSION;
import static cn.axzo.workflow.common.enums.BpmnCountersignTypeEnum.BACK_COUNTERSIGN;
import static cn.axzo.workflow.common.enums.BpmnCountersignTypeEnum.FORWARD_COUNTERSIGN;
/**
* 在框架提供 Web 服务前恢复动态前后加签的流程实例的内存中的模型
@ -51,7 +49,7 @@ public class RecoverDynamicCounterSignProcess implements SmartLifecycle {
@Override
public void start() {
// TODO 恢复动态会签中的流程实例
// 恢复动态会签中的流程实例
log.info("executing before web server start");
// 查询有过加签的审批实例
List<ExtAxDynamicSignRecord> list = extAxDynamicSignRecordService.list();
@ -85,7 +83,6 @@ public class RecoverDynamicCounterSignProcess implements SmartLifecycle {
)
));
RepositoryService repositoryService = springProcessEngineConfiguration.getRepositoryService();
DeploymentManager deploymentManager = springProcessEngineConfiguration.getDeploymentManager();
grouped.forEach((k, v) -> {
if (runningProcessIds.contains(k)) {
@ -94,19 +91,15 @@ public class RecoverDynamicCounterSignProcess implements SmartLifecycle {
ProcessDefinition processDefinitionEntity = deploymentManager.findDeployedProcessDefinitionById(v.get(0).getProcessDefinitionId());
Process process = deploymentManager.resolveProcessDefinition(processDefinitionEntity).getProcess();
v.forEach(sign -> {
if (Objects.equals(sign.getCounterSignType(), FORWARD_COUNTERSIGN.getType())) {
UserTask originalUserTask = (UserTask) process.getFlowElement(sign.getOriginalActivityId());
BpmnFlowNodeMode nodeMode = Objects.equals(originalUserTask.getLoopCharacteristics().getCompletionCondition(), AND_SIGN_EXPRESSION) ? BpmnFlowNodeMode.AND : BpmnFlowNodeMode.OR;
if (StringUtils.hasText(sign.getTargetNodeMode())) {
nodeMode = BpmnFlowNodeMode.valueOfType(sign.getTargetNodeMode());
}
// 创建前加签节点
UserTask newUserTask = CustomBpmnModelHelper.createUserTask(springProcessEngineConfiguration, originalUserTask, sign.getTargetActivityId(), nodeMode, sign.getAssignerList());
process.addFlowElement(newUserTask);
CustomBpmnModelHelper.rewireSequenceFlows(process, originalUserTask, newUserTask);
} else if (Objects.equals(sign.getCounterSignType(), BACK_COUNTERSIGN.getType())) {
UserTask originalUserTask = (UserTask) process.getFlowElement(sign.getOriginalActivityId());
BpmnFlowNodeMode nodeMode = Objects.equals(originalUserTask.getLoopCharacteristics().getCompletionCondition(), AND_SIGN_EXPRESSION) ? BpmnFlowNodeMode.AND : BpmnFlowNodeMode.OR;
if (StringUtils.hasText(sign.getTargetNodeMode())) {
nodeMode = BpmnFlowNodeMode.valueOfType(sign.getTargetNodeMode());
}
// 创建加签节点
UserTask newUserTask = CustomBpmnModelHelper.createUserTask(springProcessEngineConfiguration, originalUserTask, sign.getTargetActivityId(), nodeMode, sign.getAssignerList());
process.addFlowElement(newUserTask);
CustomBpmnModelHelper.rewireSequenceFlows(process, originalUserTask, newUserTask, Objects.requireNonNull(BpmnCountersignTypeEnum.valueOfType(sign.getCounterSignType())));
});
}
});