自定义流程,通过BpmnModel对象动态生成流程并在act_ge_bytearray中生成对应的流程图,CustomProcess为自定义类封装了流程名称和流程usertask,一下代码默认添加的都是usertask任务。
import com.gosun.sacw.activiti.model.CustomProcess;
import com.gosun.sacw.activiti.model.CustomProcessNode;
import com.gosun.sacw.sacw.uitl.BHUtil;
import org.activiti.bpmn.model.*;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.validation.ProcessValidator;
import org.activiti.validation.ProcessValidatorFactory;
import org.activiti.validation.ValidationError;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component("processDeployBuilder")
public class ProcessDeployBuilder {
public static final String STARTEVENT = "startEvent";
public static final String ENDEVENT = "endEvent";
// public static final String PARALLELGATEWAY = "parallelGateway";
// public static final String USERTASK = "userTask";
// public static final String SEQUENCEFLOW = "sequenceFlow";
// public static final String SERVICETASK = "serviceTask";
// public static final String EXCLUSIVEGATEWAY = "exclusiveGateway";
public static final double STARTEVENT_X = 50D;
public static final double STARTEVENT_Y = 75D;
public static final double STARTEVENT_HEIGHT = 30D;
public static final double STARTEVENT_WIDTH = 30D;
public static final double USERTASK_Y = 40D;
public static final double USERTASK_HEIGHT = 100D;
public static final double USERTASK_WIDTH = 80D;
public static final double EVENT_OFFSET = 100D;//偏移量
@Autowired
private RepositoryService repositoryService;
public String deployProcess(CustomProcess customProcess) throws Exception {
BpmnModel bpmnModel = new BpmnModel();
String processId = "proc_" + BHUtil.uuid();
org.activiti.bpmn.model.Process bpmnProcess = new org.activiti.bpmn.model.Process();
bpmnProcess.setId(processId);
bpmnProcess.setName(customProcess.getName());
//开始事件
StartEvent startEvent = generateStartEvent(bpmnModel);
bpmnProcess.addFlowElement(startEvent);
String preEventId = startEvent.getId();
List<CustomProcessNode> customProcessNodes = customProcess.getNodes();
int userTaskNum = customProcessNodes.size();
for (int i = 0; i < userTaskNum; i++) {
CustomProcessNode customProcessNode = customProcessNodes.get(i);
UserTask userTask = generateUserTask(bpmnModel, customProcessNode, i + 1);
bpmnProcess.addFlowElement(userTask);
SequenceFlow sequenceFlow = generateSequenceFlow(bpmnModel, preEventId, userTask.getId(), i + 1);
bpmnProcess.addFlowElement(sequenceFlow);
preEventId = userTask.getId();
}
EndEvent endEvent = generateEndEvent(bpmnModel, userTaskNum + 1);
bpmnProcess.addFlowElement(endEvent);
SequenceFlow sequenceFlow = generateSequenceFlow(bpmnModel, preEventId, endEvent.getId(), userTaskNum + 1);
bpmnProcess.addFlowElement(sequenceFlow);
bpmnModel.addProcess(bpmnProcess);
//校验流程
ProcessValidatorFactory processValidatorFactory = new ProcessValidatorFactory();
ProcessValidator defaultProcessValidator = processValidatorFactory.createDefaultProcessValidator();
List<ValidationError> validate = defaultProcessValidator.validate(bpmnModel);
//流程校验失败
if (validate.size() > 0) {
throw new RuntimeException("流程部署异常,异常信息:" + validate);
}
//部署流程
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
deploymentBuilder.key(processId).name("请假审批流程");
deploymentBuilder.addBpmnModel(processId + ".bpmn20.xml", bpmnModel);
deploymentBuilder.deploy();
return processId;
}
//开始节点
public StartEvent generateStartEvent(BpmnModel bpmnModel) {
StartEvent startEvent = new StartEvent();
startEvent.setId(STARTEVENT);
startEvent.setName("流程申请");
GraphicInfo graphicInfo = generateGraphicInfo(STARTEVENT_X, STARTEVENT_Y, STARTEVENT_WIDTH, STARTEVENT_HEIGHT);
bpmnModel.addGraphicInfo(STARTEVENT, graphicInfo);
return startEvent;
}
//用户任务,根据提交的名称和角色信息生成
public UserTask generateUserTask(BpmnModel bpmnModel, CustomProcessNode customProcessNode, int index) {
String taskId = "task_" + BHUtil.uuid();
UserTask userTask = new UserTask();
userTask.setId(taskId);
userTask.setName(customProcessNode.getName());
//设置当前任务审批角色
userTask.setCandidateGroups(customProcessNode.getAssignment());
GraphicInfo graphicInfo = generateGraphicInfo(generateEventOffset(index), USERTASK_Y, USERTASK_WIDTH, USERTASK_HEIGHT);
bpmnModel.addGraphicInfo(taskId, graphicInfo);
return userTask;
}
//结束节点 默认 空结束事件,即最后一个任务审批完毕后流程实例自动结束
public EndEvent generateEndEvent(BpmnModel bpmnModel, int eventNum) {
EndEvent endEvent = new EndEvent();
endEvent.setId(ENDEVENT);
endEvent.setName("流程结束");
GraphicInfo graphicInfo = generateGraphicInfo(generateEventOffset(eventNum), STARTEVENT_Y, STARTEVENT_WIDTH, STARTEVENT_HEIGHT);
bpmnModel.addGraphicInfo(ENDEVENT, graphicInfo);
return endEvent;
}
//节点连接线
public SequenceFlow generateSequenceFlow(BpmnModel bpmnModel, String sourceRef, String targetRef, int index) {
//节点连接线开始位置:开始节点的起始位置 + 开始节点的宽度 + (节点偏移量(连接线长度) + 用户任务宽度) * (当前用户任务下标 - 1)
Double startIndex = STARTEVENT_X + STARTEVENT_WIDTH + (EVENT_OFFSET + USERTASK_WIDTH) * (index - 1);
String flowId = "flow" + BHUtil.uuid();
SequenceFlow sequenceFlow = new SequenceFlow();
sequenceFlow.setId(flowId);
sequenceFlow.setSourceRef(sourceRef);
sequenceFlow.setTargetRef(targetRef);
//流程图信息
GraphicInfo graphicInfo1 = generateGraphicInfo(startIndex, 50L, 30L, 30L);
GraphicInfo graphicInfo2 = generateGraphicInfo(startIndex + EVENT_OFFSET, 50L, 30L, 30L);
List<GraphicInfo> graphicInfoList = new ArrayList<>();
graphicInfoList.add(graphicInfo1);
graphicInfoList.add(graphicInfo2);
bpmnModel.addFlowGraphicInfoList(flowId, graphicInfoList);
return sequenceFlow;
}
//生成流程图位置信息
public GraphicInfo generateGraphicInfo(double x, double y, double width, double height) {
GraphicInfo graphicInfo = new GraphicInfo();
graphicInfo.setX(x);
graphicInfo.setY(y);
graphicInfo.setWidth(width);
graphicInfo.setHeight(height);
return graphicInfo;
}
//获取节点x轴起始位置
public Double generateEventOffset(int index) {
//开始节点的起始位置 + 开始节点的宽度 + (节点偏移量(连接线长度)*当前用户任务下标 + 用户任务宽度*(当前用户任务下标 - 1)
return STARTEVENT_X + STARTEVENT_WIDTH + EVENT_OFFSET * index + USERTASK_WIDTH * (index - 1);
}
}
生成的流程图出现中文乱码,网上查找原因需要配置SpringProcessEngineConfiguration,但都是以xml配置,不适合spring boot项目,调试源码,发现生成spring boot 自动配置SpringProcessEngineConfiguration对象时调用AbstractProcessEngineAutoConfiguration.baseSpringProcessEngineConfiguration(…),改方法会注入ProcessEngineConfigurationConfigurer对象,并执行配置
if (this.processEngineConfigurationConfigurer != null) {
this.processEngineConfigurationConfigurer.configure(conf);
}
由此自定义ProcessEngineConfigurationConfigurer子类,设置字体并注入spring容器
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
import org.springframework.stereotype.Component;
/**
* AbstractProcessEngineAutoConfiguration.baseSpringProcessEngineConfiguration
* 构建流程引擎配置信息时会注入ProcessEngineConfigurationConfigurer子类,通过此处自定义配置信息
*/
@Component("customProcessEngineConfigurationConfigurer")
public class CustomProcessEngineConfigurationConfigurer implements ProcessEngineConfigurationConfigurer {
private static final String FONT_NAME = "宋体";
@Override
public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {
springProcessEngineConfiguration.setActivityFontName(FONT_NAME);
springProcessEngineConfiguration.setLabelFontName(FONT_NAME);
springProcessEngineConfiguration.setAnnotationFontName(FONT_NAME);
}
}