activiti新增自定义节点的几个步骤
最近在搞一个工作流的程序,需要添加审批节点,不需要用户添加排他网关设置条件就能进行回退至上一节点或进入下一节点。
通常我们实现审批的话,需要添加如下流程图
用户操作起来比较麻烦,因此希望添加一个节点不需要在添加单一网关就可以实现上图的功能,如下图
界面上用户只需要点击通过和驳回就能够实现审批流程。
需求分析
需要实现单人审批和多人审批节点,其中多人审批分为会签和或签
会签
会签:“会签”是一个汉语词汇,拼音为 huì
qiān。它的基本含义是联合签署,通常用于多个领导或部门共同签署一份文件或协议的场景。在办公流程中,会签通常意味着一份文件需要多个部门或领导的审批和签字,只有当所有相关部门或领导都签字同意后,文件才能继续流转或生效。例如,在一个企业中,如果要采购一批设备,可能需要财务、采购、技术等多个部门的会签,以确保采购计划符合企业的财务预算、技术要求和采购政策。
此外,“会签”在不同的领域和语境中可能有不同的含义和用法。在某些情况下,它可能只是简单地指两个或多个人同时签署一份文件。因此,在具体使用时需要根据上下文和语境来理解其准确含义。
或签
“或签”这个词在流程审批的语境中,通常指的是在多个审批人或者多个审批环节中,只需要其中一个人或一个环节审批通过,整个流程就可以继续进行下去,而不需要所有审批人都必须审批通过。这与“会签”不同,会签是要求所有审批人都必须审批通过。
例如,在一个请假流程中,如果设置了直接主管和部门经理为或签关系,那么员工提交的请假申请只需要直接主管或者部门经理其中一个人审批通过,就可以完成整个请假流程。
“或签”的设置可以使得审批流程更加灵活和高效,避免因为某个审批人无法及时处理而导致整个流程被延误。但需要注意的是,在某些情况下,或签也可能带来一些风险,比如可能会导致不同审批人之间的意见不一致,从而影响决策的正确性。因此,在设置或签时,需要根据实际情况进行权衡和考虑。
实现路径
1. bpmn xml定义修改
2. 节点定义解析修改
单人审批
继承UserTask
public class SingleApproveTask extends UserTask {
}
继承UserTaskXMLConverter类,重写getXMLElementName方法
@Override
protected String getXMLElementName() {
return SINGLE_APPROVE_TASK;
}
@Override
public Class<? extends BaseElement> getBpmnElementType() {
return SingleApproveTask.class;
}
@Override
protected BaseElement convertXMLToElement(XMLStreamReader xtr, BpmnModel model) throws Exception {
String formKey = xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_FORM_FORMKEY);
UserTask userTask = new SingleApproveTask();
BpmnXMLUtil.addXMLLocation(userTask, xtr);
userTask.setDueDate(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_DUEDATE));
userTask.setBusinessCalendarName(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_BUSINESS_CALENDAR_NAME));
userTask.setCategory(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_CATEGORY));
userTask.setFormKey(formKey);
userTask.setAssignee(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_ASSIGNEE));
userTask.setOwner(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_OWNER));
userTask.setPriority(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_PRIORITY));
if (StringUtils.isNotEmpty(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_CANDIDATEUSERS))) {
String expression = xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_CANDIDATEUSERS);
userTask.getCandidateUsers().addAll(parseDelimitedList(expression));
}
if (StringUtils.isNotEmpty(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_CANDIDATEGROUPS))) {
String expression = xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_CANDIDATEGROUPS);
userTask.getCandidateGroups().addAll(parseDelimitedList(expression));
}
userTask.setExtensionId(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_SERVICE_EXTENSIONID));
if (StringUtils.isNotEmpty(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_SKIP_EXPRESSION))) {
String expression = xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_SKIP_EXPRESSION);
userTask.setSkipExpression(expression);
}
BpmnXMLUtil.addCustomAttributes(xtr, userTask, defaultElementAttributes,
defaultActivityAttributes, defaultUserTaskAttributes);
parseChildElements(getXMLElementName(), userTask, childParserMap, model, xtr);
return userTask;
}
3. 自定义节点行为
控制用户执行驳回操作时,节点离开的方向
继承UserTaskActivityBehavior,重写leave方法
@Override
public void leave(DelegateExecution execution) {
//判断是否为驳回
String condition = ActivitiUtil.getRejectVariableName(execution.getCurrentActivityId());
if (execution.getVariable(condition) != null && (Boolean) execution.getVariable(condition)) {
//驳回
String rejectNodeId = execution.getVariable(ActivitiUtil.getRejectNodeIdVariableName(execution.getCurrentActivityId()), String.class);
//创建虚拟连线
Process mainProcess = bpmnParse.getBpmnModel().getMainProcess();
FlowElement flowElement = mainProcess.getFlowElement(rejectNodeId);
FlowElement currentFlowElement = execution.getCurrentFlowElement();
if (currentFlowElement instanceof FlowNode) {
List<SequenceFlow> outgoingFlows = ((FlowNode) currentFlowElement).getOutgoingFlows();
for (SequenceFlow outgoingFlow : outgoingFlows) {
outgoingFlow.setTargetFlowElement(flowElement);
}
}
}
super.leave(execution);
}
4. 实现类ProcessEngineConfigurationConfigurer中的configure方法
BpmnXMLConverter.addConverter(new SingleApproveTaskXMLConverter());
BpmnXMLConverter.addConverter(new MultiApproveTaskXMLConverter());
List<BpmnParseHandler> postBpmnParseHandlers = processEngineConfiguration.getProcessEngineConfiguration().getPostBpmnParseHandlers();
if (postBpmnParseHandlers == null) {
postBpmnParseHandlers = new ArrayList<>();
}
postBpmnParseHandlers.add(new SingleApproveTaskParseHandler());
postBpmnParseHandlers.add(new MultiApproveTaskParseHandler());
postBpmnParseHandlers.add(new UserTaskParseHandler());
processEngineConfiguration.getProcessEngineConfiguration().setPostBpmnParseHandlers(postBpmnParseHandlers);