项目中用到使用flowable生成动态流程图的地方,所以用笔记记下来
/**
* 动态创建流程图
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class FlowableBpmnHandler {
private RepositoryService repositoryService;
//最少流程节点数量
private static final Integer MIN_NODE_SIZE = 2;
/**
* 根据流程信息和节点配置创建流程图
* 传入map
*
* @return
*/
public void createFlowableBpmn(Map<String,String> map) {
//流程名称、流程id,流程类型、
if (null == map.get("procinstId")|| StringUtils.isBlank(map.get("procinstName")) || StringUtils.isBlank(map.get("procinstTypeName")) || StringUtils.isBlank(map.get("procinstType"))) {
throw new RuntimeException("procinstId、procinstName、procinstTypeName、ProcinstType不允许为空");
}
//数据校验:传入的数据节点字段:流程配置id、节点id、节点顺序、节点描述
List<MgProcinstNode> nodeList = map.get("procinstNode");
if (ListUtil.isEmpty(nodeList) || nodeList.size() < MIN_NODE_SIZE) {
throw new RuntimeException("流程节点不能少于两个,请配置发起人节点和至少一个审批节点");
}
//流程配置节点主键id过滤
List<Long> ids = nodeList.stream().map(MgProcinstNode::getProcinstNodeId).distinct().filter(Objects::nonNull).collect(Collectors.toList());
if (ListUtil.isEmpty(ids) || ids.size() < nodeList.size()){
throw new RuntimeException("请检查node节点的主键id是否为空");
}
/*
* 整合节点和连线成为一个 process
*/
Process process = new Process();
// 流程标识
process.setId("pro_" + map.get("procinstId"));
process.setName(map.get("procinstName"));
//开始事件
FlowElement startEvent = createStartFlowElement("start","开始");
process.addFlowElement(startEvent);
//结束事件--任务正常完成
FlowElement completeEvent = createEndFlowElement("complete","结束");
List<UserTask> userTaskList = createCommonUserTask(nodeList);
//发起人节点
UserTask startUserTask = userTaskList.get(0);
for (int i = 0; i < userTaskList.size(); i++) {
UserTask userTask = userTaskList.get(i);
process.addFlowElement(userTask);
//发起人到第一个节点不需要网关
if(i == 0) {
// 开始节点到发起人节点
SequenceFlow startSequenceFlow = new SequenceFlow(startEvent.getId(), userTask.getId());
process.addFlowElement(startSequenceFlow);
// 发起人节点到审批节点
UserTask nextUserTask = userTaskList.get(i + 1);
SequenceFlow sequenceFlow = new SequenceFlow(userTask.getId(), nextUserTask.getId());
process.addFlowElement(sequenceFlow);
} else {
//从发起人后的第一个节点开始增加网关节点
ExclusiveGateway exclusiveGateway = createGateway(i,userTask.getName());
process.addFlowElement(exclusiveGateway);
//审批节点到网关节点
SequenceFlow sequenceFlow = new SequenceFlow(userTask.getId(), exclusiveGateway.getId());
sequenceFlow.setName(userTask.getName()+ "开始审批");
process.addFlowElement(sequenceFlow);
// 取下一步的节点id,可能为结束节点或者审批节点
String nextUserTaskId = completeEvent.getId();
if (userTaskList.size() - i > 1 ){
nextUserTaskId = userTaskList.get(i + 1).getId();
}
//网关节点审批通过继续下一步
SequenceFlow sequenceFlowAgree = new SequenceFlow(exclusiveGateway.getId(), nextUserTaskId);
sequenceFlowAgree.setConditionExpression("${agree == 1}");
sequenceFlowAgree.setName("同意");
process.addFlowElement(sequenceFlowAgree);
//网关节点审批拒绝返回到发起人,认领直接终止流程
SequenceFlow sequenceFlowRefuse = new SequenceFlow(exclusiveGateway.getId(),startUserTask.getId());
sequenceFlowRefuse.setConditionExpression("${agree == 0}");
sequenceFlowRefuse.setName("拒绝");
process.addFlowElement(sequenceFlowRefuse);
}
}
process.addFlowElement(completeEvent);
BpmnModel bpmnModel = new BpmnModel();
bpmnModel.addProcess(process);
// 自动生成布局 布局节点位置
new BpmnAutoLayout(bpmnModel).execute();
BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
byte[] convertToXML = bpmnXMLConverter.convertToXML(bpmnModel);
String bytes = new String(convertToXML);
log.info("该流程的流程xml为:{}", bytes);
ProcessValidatorFactory processValidatorFactory = new ProcessValidatorFactory();
ProcessValidator defaultProcessValidator = processValidatorFactory.createDefaultProcessValidator();
// 验证失败信息的封装ValidationError
List<ValidationError> validate = defaultProcessValidator.validate(bpmnModel);
log.error("获取到的验证信息为:{}", validate);
// if (ListUtil.isNotEmpty(validate) ){
// throw new RuntimeException("流程有误,请检查后重试");
// }
// 流程部署
Deployment deploy = repositoryService.createDeployment()
.tenantId("1099")
.addString(map.get("procinstName")+".bpmn", bytes)
.category(map.get("procinstTypeName")).deploy();
log.info("部署id:" + deploy.getId());
}
/**
* 创建开始节点信息
* @return
*/
public FlowElement createStartFlowElement(String id,String name){
StartEvent startEvent = new StartEvent();
startEvent.setId(id);
startEvent.setName(name);
startEvent.setInitiator("initiator");
return startEvent;
}
/**
* 创建结束节点信息
* @param id
* @param name
* @return
*/
public FlowElement createEndFlowElement(String id,String name){
EndEvent endEvent=new EndEvent();
endEvent.setId(id);
endEvent.setName(name);
return endEvent;
}
/**
* 创建普通任务节点信息
* @param mgProcinstNodes 节点信息
* @return
*/
public List<UserTask> createCommonUserTask(List<MgProcinstNode> mgProcinstNodes){
List<MgProcinstNode> nodeList = mgProcinstNodes.stream().sorted((u1, u2) -> u1.getNodeSort().compareTo(u2.getNodeSort())).collect(Collectors.toList());
List<UserTask> userTaskList = new ArrayList<>();
for (int i = 0; i < nodeList.size(); i++) {
MgProcinstNode node = nodeList.get(i);
UserTask userTask = new UserTask();
userTask.setId("task_" + node.getProcinstNodeId());
userTask.setName(node.getRoleName());
if (i > 0) {
userTask.setName(userTask.getName());
}else {
userTask.setAssignee("${initiator}");
}
userTaskList.add(userTask);
}
return userTaskList;
}
/**
* 创建监听类
* @return
*/
public List<FlowableListener> CreateTaskListeners(){
List<FlowableListener> taskListeners = new ArrayList<>();
FlowableListener listener = new FlowableListener();
listener.setEvent("all");
listener.setImplementationType("delegateExpression");
listener.setImplementation("${permissinTaskListener}");
//listener.setId("CATEGORYID_FILE_AUDIT_listener");
taskListeners.add(listener);
return taskListeners;
}
/**
* 创建拍他网关节点
* @return
*/
private ExclusiveGateway createGateway(Integer i,String name){
StringBuilder gateWayId = new StringBuilder(FlowableCommonConstant.GATEWAY_PREFIX);
ExclusiveGateway gateway = new ExclusiveGateway();
gateway.setName(name + "审批");
gateway.setId(gateWayId.append(i).toString());
return gateway;
}
}
生成的流程图的样式,可以在控制中打印的xml中查看,也可以修改生成方式,直接下载