Flowable用代码自定义流程

项目集成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;

    }
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容