项目集成flowable有段时间了,发现行业的流程业务全是一条直线加3,4个人工审核的节点,并没有分支网关等其它节点。因此原来用的model设计器组件多参数复杂,客户现场的体验不是很好。所以根据现场的实际业务自己调api的方式实现个简单的流程设计器。
最终的实现的效果是由开始节点[StartEvent]和结束结点[EndEvent]及用户任务节点[UserTask]组成的直线审核流程,并兼容用原生的流程设计器打开编辑。最终的效果如下图所示:
最终的效果
前端界面设计介绍,用户界面可以增加节点,删除节点。对节点命名,定义节点的审核用户[普通用户或角色],具体效果图如下所示:
用户界面
定义收集前端参数的对象,具体代码如下所示
@Data
public class ProcessParam {
//流程ID
private String modelId;
//流程定义key
private String key;
//流程名字
private String name;
//流程节点
private List<TaskParam> tasks;
//开始结点名称
private String startNodeName;
//结束节点名称
private String endNodeName;
}
@Data
public class TaskParam {
public TaskParam(){
}
public TaskParam(String name,String no,String userType,List<String> userOrRoleIds,String userOrRoleName){
this.name = name;
this.no = no;
this.userType = userType;
this.userOrRoleIds = userOrRoleIds;
this.userOrRoleName = userOrRoleName;
}
//节点名称
private String name;
//节点编号
private String no;
//审核用户类型 用户(user)或角色(role)
private String userType;
//审核用户或角色id
private List<String> userOrRoleIds;
//用户或角色名称
private String userOrRoleName;
}
保存流程设计器的数据,具体的代妈如下:
大体思路是,构建一个BpmnModel对象,构建出各节点对象并设置好相关的参数,如名称,编号,审核人等,用SequenceFlow对象按顺序连接各节点对象,特别注意要设置各节点对象的outgoing属性网上很多教程没有提到这个,不设置会导致连接不正确。最后用BpmnAutoLayout对象自动设置好节点的坐标信息,不然也会导致流程在原生设计器显示不正常。
public void saveModelData(ProcessParam processParam){
Model model = modelService.getModel(processParam.getModelId());
Process process = new Process();
process.setId(model.getKey());
process.setName(model.getName());
BpmnModel bpmnModelNew = new BpmnModel();
bpmnModelNew.addProcess(process);
List<TaskParam> nodeList = processParam.getTasks();
//开始事件
StartEvent startEvent = new StartEvent();
startEvent.setId("start");
startEvent.setName(processParam.getStartNodeName());
process.addFlowElement(startEvent);
FlowElement waitNode = startEvent;
int i = 0;
for ( ;i < nodeList.size(); i++) {
TaskParam taskParam = nodeList.get(i);
UserTask userTask = new UserTask();
userTask.setId((i+1)+"");
userTask.setName(taskParam.getName());
if("user".equals(taskParam.getUserType())){
userTask.setCandidateUsers(taskParam.getUserOrRoleIds());
}
if("role".equals(taskParam.getUserType())){
userTask.setCandidateGroups(taskParam.getUserOrRoleIds());
}
process.addFlowElement(userTask);
SequenceFlow sequenceFlow = new SequenceFlow(waitNode.getId(), userTask.getId());
process.addFlowElement(sequenceFlow);
//设置当前节点的出口这个很关键,不设置这个的话生的流程图会错乱,网上很多教程也没有提到
if(i==0){
((StartEvent) waitNode).setOutgoingFlows(ListUtil.toList(sequenceFlow));
}else {
((UserTask) waitNode).setOutgoingFlows(ListUtil.toList(sequenceFlow));
}
waitNode = userTask;
}
//结束事件
EndEvent completeEvent = new EndEvent();
completeEvent.setId("complete");
completeEvent.setName(processParam.getEndNodeName());
process.addFlowElement(completeEvent);
SequenceFlow endSequenceFlow = new SequenceFlow(waitNode.getId(), completeEvent.getId());
((UserTask) waitNode).setOutgoingFlows(ListUtil.toList(endSequenceFlow));
process.addFlowElement(endSequenceFlow);
//这句很关键让程序自动部局各节点的位置,网上很多教程没有提到
new BpmnAutoLayout(bpmnModelNew).execute();
//把BpmnModel转成json格式进行保存
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
ObjectNode jsonNode = jsonConverter.convertToJson(bpmnModelNew);
modelService.saveModel(model.getId(),model.getName(),model.getKey(),model.getDescription(),jsonNode.toString(),true,"",new UserWf());
}
从数据库获取流程设计数据给到前端,具体代码如下:
大体思路是:调用api获取model对象,从model对象获取流程的json字符串然后用工具类转成BpmnModel对象,然后根据他们的关系生成ProcessParam对象传到前端去。
public ProcessParam getModelData(String modelId){
ProcessParam processParam = new ProcessParam();
List<TaskParam> tasks = new ArrayList<>();
processParam.setTasks(tasks);
Model model = modelService.getModel(modelId);
processParam.setModelId(model.getId());
processParam.setKey(model.getKey());
processParam.setName(model.getName());
//获取流程设计器的json数据
String modelEditorJson = model.getModelEditorJson();
ObjectNode editorJsonNode = null;
try {
editorJsonNode = (ObjectNode) objectMapper.readTree(modelEditorJson);
} catch (IOException e) {
e.printStackTrace();
return processParam;
}
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
//把json转成bpmnModel对象
BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorJsonNode);
Process process = bpmnModel.getMainProcess();
Map<String,FlowElement> allNode = process.getFlowElementMap();
if(allNode==null||allNode.size()<1){//没有节点信息直接返回
return processParam;
}
List<StartEvent> startEventList = process.findFlowElementsOfType(StartEvent.class);
StartEvent startEvent = startEventList.get(0);
processParam.setStartNodeName(startEvent.getName());
String nextNodeId = startEvent.getOutgoingFlows().get(0).getTargetRef();
while (allNode.get(nextNodeId)!=null){
FlowElement fe = allNode.get(nextNodeId);
if(fe instanceof UserTask){
UserTask userTask = (UserTask) fe;
List<String> userOrRoleIds = null;
String userOrRoleInfo = "";
String userType = null;
if(StrUtil.isNotBlank(userTask.getAssignee())){
userOrRoleIds = ListUtil.toList(userTask.getAssignee());
userType = "user";
}
if(userTask.getCandidateUsers()!=null&&userTask.getCandidateUsers().size()>0){
userOrRoleIds = userTask.getCandidateUsers();
userType = "user";
}
if(userTask.getCandidateGroups()!=null&&userTask.getCandidateGroups().size()>0){
userOrRoleIds = userTask.getCandidateGroups();
userType = "role";
}
//查询用户或角色名称
if ("user".equals(userType)) {
userOrRoleInfo = this.getUserInfo(userOrRoleIds);
}
if ("role".equals(userType)) {
userOrRoleInfo = this.getRoleInfo(userOrRoleIds);
}
if(userTask.getOutgoingFlows().size()>1){
throw new RuntimeException("不支持多个分支流程数据,请使用高级流程设计器");
}
tasks.add(new TaskParam(userTask.getName(),userTask.getId(),userType,userOrRoleIds,userOrRoleInfo));
nextNodeId = userTask.getOutgoingFlows().get(0).getTargetRef();
}else if(fe instanceof EndEvent){
EndEvent endEvent = (EndEvent) fe;
processParam.setEndNodeName(endEvent.getName());
nextNodeId = "";
break;
}else {
throw new RuntimeException("不支持当前流程数据,请使用高级流程设计器");
}
}
return processParam;
}