package com.casic.missiles.service.Impl.system;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.casic.missiles.dto.ReturnDTO;
import com.casic.missiles.dto.ReturnUtil;
import com.casic.missiles.dto.system.FlowDeleteRequest;
import com.casic.missiles.dto.system.FlowListRequest;
import com.casic.missiles.dto.system.FlowProcDefDTO;
import com.casic.missiles.dto.system.FlowUpdateStateRequest;
import com.casic.missiles.enums.system.AssigneeTypeEnum;
import com.casic.missiles.mapper.system.FlowDefMapper;
import com.casic.missiles.mapper.system.SystemFlowFormMapper;
import com.casic.missiles.model.system.SystemFlowForm;
import com.casic.missiles.modular.system.dao.UserMapper;
import com.casic.missiles.modular.system.model.RuRelation;
import com.casic.missiles.modular.system.model.User;
import com.casic.missiles.modular.system.service.IUserRoleRelationService;
import com.casic.missiles.service.system.IFlowDefService;
import com.casic.missiles.utils.SnowflakeUtil;
import com.casic.missiles.utils.SpringContextUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;

import static org.flowable.bpmn.model.ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION;

/**
 * @Description:
 * @Author: wangpeng
 * @Date: 2022/12/12 18:08
 */
@Slf4j
@Service
public class FlowDefDefServiceImpl implements IFlowDefService {
    @Resource
    private SnowflakeUtil snowflakeUtil;
    @Autowired
    private FlowDefMapper flowDefMapper;
    @Autowired
    private SystemFlowFormMapper flowFormMapper;
    @Resource
    private RepositoryService repositoryService;

    @Override
    @Transactional
    public ReturnDTO jsonToBpmn(JSONObject jsonObject) throws InvocationTargetException, IllegalAccessException {
        //bpmn对象（可与xml相互转化）
        BpmnModel bpmnModel = new BpmnModel();
        //流程定义对象
        Process process = new Process();
        //添加流程定义到bpmn对象
        bpmnModel.addProcess(process);
        //流程中连线
        List<SequenceFlow> sequenceFlows = Lists.newArrayList();
        //流程定义-添加流程id
        process.setId("flowableV2_" + snowflakeUtil.nextId());
        //启动事件
        StartEvent startEvent = createStartEvent();
        //流程定义-添加启动事件
        process.addFlowElement(startEvent);
        //获取json实体
        JSONObject workFlowObj = jsonObject.getJSONObject("_value");
        String name = workFlowObj.getJSONObject("workFlowDef").getString("name");
        //流程定义-添加流程名称
        process.setName(name);
        String directorId = workFlowObj.getJSONObject("workFlowDef").getString("directorId");
        //流程定义-添加扩展属性
        ExtensionAttribute extensionAttribute = new ExtensionAttribute();
        extensionAttribute.setName("Casic");
        extensionAttribute.setNamespace("http://flowable.org/bpmn");
        extensionAttribute.setValue(jsonObject.toJSONString());
        process.addAttribute(extensionAttribute);
        //流程定义-添加节点
        JSONObject processNodes = workFlowObj.getJSONObject("nodeConfig");
        String lastNode = create(startEvent.getId(), processNodes, bpmnModel, process, sequenceFlows);
        EndEvent endEvent = createEndEvent();
        process.addFlowElement(endEvent);
        process.addFlowElement(connect(lastNode, endEvent.getId(), sequenceFlows));

        //重新布局，自动生成布局，布局节点位置
        new BpmnAutoLayout(bpmnModel).execute();
        System.err.println(new String(new BpmnXMLConverter().convertToXML(bpmnModel)));

//        ProcessValidatorFactory processValidatorFactory = new ProcessValidatorFactory();
//        ProcessValidator defaultProcessValidator = processValidatorFactory.createDefaultProcessValidator();
//        //验证失败信息的封装ValidationError
//        List<ValidationError> validate = defaultProcessValidator.validate(bpmnModel);
//        log.info("jsonToBpmn获取到的流程定义验证信息为：{}", validate);
//        if (!CollectionUtils.isEmpty(validate)) {
//            throw new RuntimeException("流程有误，请检查后重试");
//        }

        //流程部署
        Deployment deploy = repositoryService.createDeployment()
                .tenantId(directorId)//租户标识符,这是一个字符串,可以是UUID、部门id
                .addBpmnModel(name + ".bpmn", bpmnModel)
                .name(name)
//                .category("")//自定义类别
                .deploy();
        log.info("流程定义部署，flowable return deploy：{}", JSONObject.toJSONString(deploy));
        if (Objects.isNull(deploy)) {
            return ReturnUtil.failed("流程定义部署失败");
        }

        //存储流程定义jsonObject，用于详情展示
        SystemFlowForm systemFlowForm = new SystemFlowForm();
        systemFlowForm.setFormId(workFlowObj.getString("formId"));
        systemFlowForm.setDeployId(deploy.getId());
        systemFlowForm.setProcDefId(process.getId());
        systemFlowForm.setFormName(workFlowObj.getString("formName"));
        systemFlowForm.setFormContent(workFlowObj.getString("formContent"));
        systemFlowForm.setFormDesc(workFlowObj.getString("formDesc"));
        systemFlowForm.setFlowDef(workFlowObj.toJSONString());
        try {
            //已设置form_id主键索引
            if (flowFormMapper.insert(systemFlowForm) <= 0) {
                //新增失败则删除刚部署的流程
                //true:允许级联删除,不设置会导致数据库外键关联异常
                repositoryService.deleteDeployment(deploy.getId(), true);
                return ReturnUtil.failed("流程定义保存失败");
            }
        } catch (DuplicateKeyException e) {
            log.error("该表单已保存过流程定义，异常信息：{}", e.getMessage());
            //新增异常则删除刚部署的流程
            repositoryService.deleteDeployment(deploy.getId(), true);
            return ReturnUtil.failed("流程定义保存异常，该表单已存在流程定义");
        } catch (Exception e) {
            log.error("流程定义保存异常，异常信息：{}", e.getMessage());
            //新增异常则删除刚部署的流程
            repositoryService.deleteDeployment(deploy.getId(), true);
            return ReturnUtil.failed("流程定义保存异常");
        }
        return ReturnUtil.success("流程定义保存成功");
    }

    @Override
    public List<FlowProcDefDTO> listPage(Page<FlowProcDefDTO> page, FlowListRequest request) {
        //查询关联表单表、表单关联部署表、用户表、流程定义表、流程部署表
        List<FlowProcDefDTO> dataList = flowDefMapper.selectDeployListPage(page, request);
        return dataList;
    }

    @Override
    @Transactional
    public ReturnDTO update(JSONObject jsonObject) throws InvocationTargetException, IllegalAccessException {
        //根据jsonObject中process的id更新流程定义的信息，流程id相同flowable会增加一个新版本的流程定义
        BpmnModel bpmnModel = new BpmnModel();
        //流程定义对象
        Process process = new Process();
        //添加流程定义到bpmn对象
        bpmnModel.addProcess(process);
        //流程中连线
        List<SequenceFlow> sequenceFlows = Lists.newArrayList();
        //启动事件
        StartEvent startEvent = createStartEvent();
        //流程定义-添加启动事件
        process.addFlowElement(startEvent);
        //获取json实体
        JSONObject workFlowObj = jsonObject.getJSONObject("_value");
        //流程定义-获取流程id
        String id = workFlowObj.getJSONObject("workFlowDef").getString("flowKey");
        process.setId(id);
        String name = workFlowObj.getJSONObject("workFlowDef").getString("name");
        String directorId = workFlowObj.getJSONObject("workFlowDef").getString("directorId");
        //流程定义-添加流程名称
        process.setName(name);
        //流程定义-添加扩展属性
        ExtensionAttribute extensionAttribute = new ExtensionAttribute();
        extensionAttribute.setName("Casic");
        extensionAttribute.setNamespace("http://flowable.org/bpmn");
        extensionAttribute.setValue(jsonObject.toJSONString());
        process.addAttribute(extensionAttribute);
        //流程定义-添加节点
        JSONObject processNodes = workFlowObj.getJSONObject("nodeConfig");
        String lastNode = create(startEvent.getId(), processNodes, bpmnModel, process, sequenceFlows);
        EndEvent endEvent = createEndEvent();
        process.addFlowElement(endEvent);
        process.addFlowElement(connect(lastNode, endEvent.getId(), sequenceFlows));

        //重新布局，自动生成布局，布局节点位置
        new BpmnAutoLayout(bpmnModel).execute();
//        System.err.println(new String(new BpmnXMLConverter().convertToXML(bpmnModel)));

//        ProcessValidatorFactory processValidatorFactory = new ProcessValidatorFactory();
//        ProcessValidator defaultProcessValidator = processValidatorFactory.createDefaultProcessValidator();
//        //验证失败信息的封装ValidationError
//        List<ValidationError> validate = defaultProcessValidator.validate(bpmnModel);
//        log.info("更新流程定义，jsonToBpmn获取到的流程定义验证信息为：{}", validate);
//        if (!CollectionUtils.isEmpty(validate)) {
//            throw new RuntimeException("流程有误，请检查后重试");
//        }

        //流程部署,更新
        Deployment deploy = repositoryService.createDeployment()
                .tenantId(directorId)//租户标识符,这是一个字符串,可以是UUID、部门id
                .addBpmnModel(name + ".bpmn", bpmnModel)
                .name(name)
//                .category()
                .deploy();
        log.info("流程定义部署更新，flowable return deploy；{}", JSONObject.toJSONString(deploy));
        if (Objects.isNull(deploy)) {
            return ReturnUtil.success("流程定义更新失败");
        }

        //存储流程定义jsonObject，用于详情展示
        SystemFlowForm systemFlowForm = new SystemFlowForm();
        systemFlowForm.setFormId(workFlowObj.getString("formId"));
        systemFlowForm.setDeployId(deploy.getId());
        systemFlowForm.setFormName(workFlowObj.getString("formName"));
        systemFlowForm.setFormContent(workFlowObj.getString("formContent"));
        systemFlowForm.setFormDesc(workFlowObj.getString("formDesc"));
        systemFlowForm.setFlowDef(workFlowObj.toJSONString());
        try {
            UpdateWrapper<SystemFlowForm> wrapper = new UpdateWrapper<>();
            wrapper.eq("form_id", workFlowObj.getString("formId"));
            if (flowFormMapper.update(systemFlowForm, wrapper) <= 0) {
                //更新失败则删除刚部署的流程
                //true:允许级联删除,不设置会导致数据库外键关联异常
                repositoryService.deleteDeployment(deploy.getId(), true);
                return ReturnUtil.failed("流程定义更新失败");
            }
        } catch (Exception e) {
            log.error("流程定义更新异常，异常信息：{}", e.getMessage());
            //更新异常则删除刚部署的流程
            repositoryService.deleteDeployment(deploy.getId(), true);
            return ReturnUtil.failed("流程定义更新异常");
        }
        return ReturnUtil.success("流程定义更新成功");
    }

    @Override
    public ReturnDTO<JSONObject> detail(String formId) {
        QueryWrapper<SystemFlowForm> wrapper = new QueryWrapper<>();
        wrapper.eq("form_id", formId);
        wrapper.eq("is_del", 0);
        SystemFlowForm systemFlowForm = flowFormMapper.selectOne(wrapper);
        if (!Objects.isNull(systemFlowForm)) {
            return ReturnUtil.success(JSONObject.parseObject(systemFlowForm.getFlowDef()));
        }
        return ReturnUtil.failed("流程定义详情获取失败，无该流程定义");
    }

    @Override
    public ReturnDTO updateState(FlowUpdateStateRequest request) {
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().deploymentId(request.getDeployId()).singleResult();
        // 激活
        if (1 == request.getState()) {
            repositoryService.activateProcessDefinitionById(procDef.getId(), true, null);
        }
        // 挂起
        if (2 == request.getState()) {
            repositoryService.suspendProcessDefinitionById(procDef.getId(), true, null);
        }
        return ReturnUtil.success();
    }

    @Override
    @Transactional
    public ReturnDTO delete(FlowDeleteRequest request) {
        //流程定义表单表删除
        QueryWrapper<SystemFlowForm> wrapper = new QueryWrapper<>();
        wrapper.eq("deploy_id", request.getDeployId());
        if (flowFormMapper.delete(wrapper) <= 0) {
            log.error("流程定义删除失败，deployId:{}", request.getDeployId());
            return ReturnUtil.failed("流程定义删除失败");
        }
        //true:允许级联删除,不设置会导致数据库外键关联异常
        repositoryService.deleteDeployment(request.getDeployId(), true);
        return ReturnUtil.success();
    }

    @Override
    public List<FlowProcDefDTO> list(FlowListRequest request) {
        //查询关联表单表、表单关联部署表、用户表、流程定义表、流程部署表
        List<FlowProcDefDTO> dataList = flowDefMapper.selectDeployList(request);
        return dataList;
    }

    @Override
    public List<FlowProcDefDTO> listByIds(List<String> ids) {
        List<FlowProcDefDTO> list = flowDefMapper.selectListByIds(ids);
        return list;
    }


    /**
     * 方法功能：根据上个节点id及本节点信息生成本节点
     *
     * @param fromId：启动事件id
     * @param flowNode：所有节点树形结构
     * @param model：bpmn对象（最终要得到的结果）
     * @param process：流程定义
     * @param sequenceFlows：连线列表
     */
    private static String create(String fromId, JSONObject flowNode, BpmnModel model, Process process, List<SequenceFlow> sequenceFlows) throws InvocationTargetException, IllegalAccessException {
        //获取节点类型，0 发起人 1审批 2抄送 3条件 4路由
        Integer nodeType = flowNode.getInteger("type");
        //节点类型-并行网关
        if (Type.PARALLEL.type.equals(nodeType)) {
            return createParallelGatewayBuilder(fromId, flowNode, model, process, sequenceFlows);
        }
        //节点类型-路由
        else if (Type.ROUTER.type.equals(nodeType)) {
            return createExclusiveGatewayBuilder(fromId, flowNode, model, process, sequenceFlows);
        }
        //节点类型-发起人（根节点）
        else if (Type.ROOT.type.equals(nodeType)) {
            //添加incoming属性（表示进来的，即上面流程节点的id列表），值为仅一个启动事件id的列表
            flowNode.put("incoming", Collections.singletonList(fromId));
            String id = createTask(flowNode, process, sequenceFlows);
            // 如果当前任务还有后续任务，则遍历创建后续任务
            JSONObject nextNode = flowNode.getJSONObject("childNode");
            if (Objects.nonNull(nextNode)) {
                FlowElement flowElement = model.getFlowElement(id);
                return create(id, nextNode, model, process, sequenceFlows);
            } else {
                return id;
            }
        }
        //节点类型-抄送任务
        else if (Type.CC.type.equals(nodeType)) {
            flowNode.put("incoming", Collections.singletonList(fromId));
            String id = createServiceTask(flowNode, process, sequenceFlows);
            // 如果当前任务还有后续任务，则遍历创建后续任务
            JSONObject nextNode = flowNode.getJSONObject("childNode");
            if (Objects.nonNull(nextNode)) {
                FlowElement flowElement = model.getFlowElement(id);
                return create(id, nextNode, model, process, sequenceFlows);
            } else {
                return id;
            }
        }
        //节点类型-用户审批任务
        else if (Type.USER_TASK.type.equals(nodeType)) {
            flowNode.put("incoming", Collections.singletonList(fromId));
            String id = createTask(flowNode, process, sequenceFlows);
            // 如果当前任务还有后续任务，则遍历创建后续任务
            JSONObject nextNode = flowNode.getJSONObject("childNode");
            if (Objects.nonNull(nextNode)) {
                FlowElement flowElement = model.getFlowElement(id);
                return create(id, nextNode, model, process, sequenceFlows);
            } else {
                return id;
            }
        } else {
            throw new RuntimeException("未知节点类型: nodeType=" + nodeType);
        }
    }

    /**
     * 排他网关分支上有条件设置
     *
     * @param formId
     * @param flowNode
     * @param model
     * @param process
     * @param sequenceFlows
     * @return 返回创建的节点的id标识
     */
    private static String createExclusiveGatewayBuilder(String formId, JSONObject flowNode, BpmnModel model, Process process, List<SequenceFlow> sequenceFlows) throws InvocationTargetException, IllegalAccessException {
        String name = flowNode.getString("nodeName");
        String exclusiveGatewayId = id("exclusiveGateway");
        ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
        exclusiveGateway.setId(exclusiveGatewayId);
        exclusiveGateway.setName(name);
        process.addFlowElement(exclusiveGateway);
        process.addFlowElement(connect(formId, exclusiveGatewayId, sequenceFlows));

        if (Objects.isNull(flowNode.getJSONArray("conditionNodes")) && Objects.isNull(flowNode.getJSONObject("childNode"))) {
            return exclusiveGatewayId;
        }
        List<JSONObject> flowNodes = Optional.ofNullable(flowNode.getJSONArray("conditionNodes")).map(e -> e.toJavaList(JSONObject.class)).orElse(Collections.emptyList());
        List<String> incoming = Lists.newArrayListWithCapacity(flowNodes.size());

        List<JSONObject> conditions = Lists.newCopyOnWriteArrayList();
        ///为每个排他网关的条件节点的子节点添加后续节点（相比于并行网关多了连线的条件表达式设置）
        for (JSONObject element : flowNodes) {
            //一个分支的子节点
            JSONObject childNode = element.getJSONObject("childNode");
            //一个分支的名字和表达式
            String nodeName = element.getString("nodeName");
            String expression = "";
            expression = element.getString("conditionExpression");
            if (StringUtils.isEmpty(expression)) {
                JSONArray nodeUserList = element.getJSONArray("nodeUserList");
                //1具体人员 2角色 3部门
                //构造条件表达式
                Iterator<Object> iterator = nodeUserList.stream().iterator();
                List<String> userIds = new ArrayList<>();
                while (iterator.hasNext()) {
                    try {
                        String s = JSONObject.toJSONString(iterator.next());
                        HashMap<String, String> map = new ObjectMapper().readValue(s, HashMap.class);
//                    HashMap<String, String> map = (HashMap<String, String>) iterator.next();
                        if (AssigneeTypeEnum.USER.equals(String.valueOf(map.get("type")))) {
                            //用户id
                            String targetId = String.valueOf(map.get("targetId"));
                            userIds.add(targetId);
                        } else if (AssigneeTypeEnum.ROLE.equals(String.valueOf(map.get("type")))) {
                            //角色id
                            String targetId = String.valueOf(map.get("targetId"));
                            IUserRoleRelationService relationService = SpringContextUtil.getBean(IUserRoleRelationService.class);
                            BaseMapper<RuRelation> baseMapper = relationService.getBaseMapper();
                            QueryWrapper<RuRelation> wrapper = new QueryWrapper<>();
                            wrapper.eq("ROLEID", targetId);
                            List<RuRelation> ruRelations = baseMapper.selectList(wrapper);
                            ruRelations.forEach(ruRelation -> {
                                userIds.add(String.valueOf(ruRelation.getUserId()));
                            });
                        } else if (AssigneeTypeEnum.DEPT.equals(String.valueOf(map.get("type")))) {
                            //部门id
                            String targetId = String.valueOf(map.get("targetId"));
                            UserMapper userMapper = SpringContextUtil.getBean(UserMapper.class);
                            QueryWrapper<User> wrapper = new QueryWrapper<>();
                            wrapper.eq("DEPT_ID", targetId);
                            List<User> users = userMapper.selectList(wrapper);
                            users.forEach(user -> {
                                userIds.add(String.valueOf(user.getId()));
                            });
                        }
                    } catch (JsonProcessingException e) {
                        log.error("构造条件分支，json转换为hashMap异常！");
                        e.printStackTrace();
                    }
                }
                String str = StringUtils.join(userIds, ",");
                // 以下已测试成功
                // ExUtils为bean实例方法，已添加
                // flowable表达式会自动识别流程变量，以下root也可识别为实例创建时的具体值，比如：${price < 100}，price就是一个流程变量的值，UEL表达式的结果为布尔值
                String starterId = "root";
                if(StringUtils.isEmpty(str)){
                    //其他条件（最后一个分支为空条件的情况）
                    expression = "#{exUtils." + "strContains(" + starterId + "," + starterId + ")}";
                }else {
                    expression = "#{exUtils." + "strContains(" + starterId + "," + str + ")}";
                }
            }

            if (Objects.isNull(childNode)) {
                //条件节点下无子节点，设置后续节点的fromId为exclusiveGatewayId（即排他网关的id，多个分支的排他网关id相同，名字和表达式不同）
                incoming.add(exclusiveGatewayId);
                JSONObject condition = new JSONObject();
                condition.fluentPut("nodeName", nodeName)
                        .fluentPut("expression", expression);
                conditions.add(condition);
                continue;
            }
            // 只生成一个任务，同时设置当前任务的条件
            childNode.put("incoming", Collections.singletonList(exclusiveGatewayId));
            String identifier = create(exclusiveGatewayId, childNode, model, process, sequenceFlows);
            //连线列表中前一个节点是exclusiveGatewayId的连线，给每个连线设置名称和表达式
            //使用forEach是因为前一个节点是exclusiveGatewayId的节点有很多，需要遍历为没赋值过的赋值
            List<SequenceFlow> flows = sequenceFlows.stream().filter(flow -> StringUtils.equals(exclusiveGatewayId, flow.getSourceRef()))
                    .collect(Collectors.toList());
            for (SequenceFlow e : flows) {
                //设置连线名为条件节点名
                if (StringUtils.isBlank(e.getName()) && StringUtils.isNotBlank(nodeName)) {
                    e.setName(nodeName);
                }
                // 设置条件表达式
                if (Objects.isNull(e.getConditionExpression()) && StringUtils.isNotBlank(expression)) {
                    e.setConditionExpression(expression);
                }
            }
            if (Objects.nonNull(identifier)) {
                incoming.add(identifier);
            }
        }
        //当前网关节点的子节点（整个排他网关的子节点）
        JSONObject childNode = flowNode.getJSONObject("childNode");

        if (Objects.nonNull(childNode)) {
            //有条件分支但无条件节点对应的子节点
            if (incoming == null || incoming.isEmpty()) {
                return create(exclusiveGatewayId, childNode, model, process, sequenceFlows);
            } else {
                // 所有 service task 连接 end exclusive gateway
                //条件分支汇合到下面的childNode节点
                childNode.put("incoming", incoming);
                FlowElement flowElement = model.getFlowElement(incoming.get(0));
                // 1.0 先进行边连接, 暂存 nextNode
                JSONObject nextNode = childNode.getJSONObject("childNode");
                //当前网关的结束网关
                String endExId = id("exclusiveGateway") + "end";
                process.addFlowElement(createExclusiveGateWayEnd(endExId));
                childNode.put("childNode", null);
                String identifier = endExId;
                for (int i = 0; i < incoming.size(); i++) {
                    // TODO: 2022/12/5 此处用的connect第一个参数和并行网关的为啥不一样
                    // TODO: 2022/12/5 为啥有后续节点这里却设置成了endExId
                    process.addFlowElement(connect(incoming.get(i), endExId, sequenceFlows));
                }

                //  针对 gateway 空任务分支 添加条件表达式，即条件节点下无子节点的情况
                if (!conditions.isEmpty()) {
                    FlowElement flowElement1 = model.getFlowElement(identifier);
                    // 获取从 gateway 到目标节点 未设置条件表达式的节点
                    List<SequenceFlow> flows = sequenceFlows.stream().filter(flow -> StringUtils.equals(flowElement1.getId(), flow.getTargetRef()))
                            .filter(flow -> StringUtils.equals(flow.getSourceRef(), exclusiveGatewayId))
                            .collect(Collectors.toList());
                    //顺序无所谓，都是分支上一个条件的分支
                    flows.stream().forEach(sequenceFlow -> {
                        if (!conditions.isEmpty()) {
                            JSONObject condition = conditions.get(0);
                            String nodeName = condition.getString("nodeName");
                            String expression = condition.getString("expression");

                            if (StringUtils.isBlank(sequenceFlow.getName()) && StringUtils.isNotBlank(nodeName)) {
                                sequenceFlow.setName(nodeName);
                            }
                            // 设置条件表达式
                            if (Objects.isNull(sequenceFlow.getConditionExpression()) && StringUtils.isNotBlank(expression)) {
                                sequenceFlow.setConditionExpression(expression);
                            }

                            conditions.remove(0);
                        }
                    });

                }

                // 1.1 边连接完成后，在进行 nextNode 创建
                if (Objects.nonNull(childNode)) {
                    return create(endExId, childNode, model, process, sequenceFlows);
                } else {
                    return endExId;
                }
            }
        } else {
            // 所有 service task 连接 end exclusive gateway
            // 1.0 先进行边连接, 暂存 nextNode
            String endExId = id("exclusiveGateway") + "end";
            process.addFlowElement(createExclusiveGateWayEnd(endExId));
            String identifier = endExId;
            //其他条件分支和第一个条件分支连线的终点节点相同
            for (int i = 0; i < incoming.size(); i++) {
                process.addFlowElement(connect(incoming.get(i), endExId, sequenceFlows));
            }
            //  针对 gateway 空任务分支 添加条件表达式，即条件节点下无子节点的情况
            if (!conditions.isEmpty()) {
                FlowElement flowElement1 = model.getFlowElement(identifier);
                // 获取从 gateway 到目标节点 未设置条件表达式的节点
                List<SequenceFlow> flows = sequenceFlows.stream().filter(flow -> StringUtils.equals(flowElement1.getId(), flow.getTargetRef()))
                        .filter(flow -> StringUtils.equals(flow.getSourceRef(), exclusiveGatewayId))
                        .collect(Collectors.toList());
                flows.stream().forEach(sequenceFlow -> {
                    if (!conditions.isEmpty()) {
                        JSONObject condition = conditions.get(0);
                        String nodeName = condition.getString("nodeName");
                        String expression = condition.getString("expression");

                        if (StringUtils.isBlank(sequenceFlow.getName()) && StringUtils.isNotBlank(nodeName)) {
                            sequenceFlow.setName(nodeName);
                        }
                        // 设置条件表达式
                        if (Objects.isNull(sequenceFlow.getConditionExpression()) && StringUtils.isNotBlank(expression)) {
                            sequenceFlow.setConditionExpression(expression);
                        }
                        conditions.remove(0);
                    }
                });

            }
            return endExId;
        }
    }

    /**
     * 并行网关分支上无条件设置
     *
     * @param formId
     * @param flowNode
     * @param model
     * @param process
     * @param sequenceFlows
     * @return 返回创建的节点的id标识
     */
    private static String createParallelGatewayBuilder(String formId, JSONObject flowNode, BpmnModel model, Process process, List<SequenceFlow> sequenceFlows) throws InvocationTargetException, IllegalAccessException {
        //并行网关节点名
        String name = flowNode.getString("nodeName");
        ParallelGateway parallelGateway = new ParallelGateway();
        String parallelGatewayId = id("parallelGateway");
        parallelGateway.setId(parallelGatewayId);
        parallelGateway.setName(name);
        process.addFlowElement(parallelGateway);
        //与上个节点的连线
        process.addFlowElement(connect(formId, parallelGatewayId, sequenceFlows));
        //1、无条件节点和子节点
        if (Objects.isNull(flowNode.getJSONArray("conditionNodes")) && Objects.isNull(flowNode.getJSONObject("childNode"))) {
            return parallelGatewayId;
        }
        //2、有条件节点或子节点
        //获取条件节点
        List<JSONObject> flowNodes = Optional.ofNullable(flowNode.getJSONArray("conditionNodes")).map(e -> e.toJavaList(JSONObject.class)).orElse(Collections.emptyList());
        //每个分支上的最后节点的标识id，用于给整个并行网关的子节点提供fromId
        List<String> incoming = Lists.newArrayListWithCapacity(flowNodes.size());
        //为每个并行网关的条件节点的子节点添加后续节点
        for (JSONObject element : flowNodes) {
            JSONObject childNode = element.getJSONObject("childNode");
            if (Objects.isNull(childNode)) {
                incoming.add(parallelGatewayId);
                continue;
            }
            String identifier = create(parallelGatewayId, childNode, model, process, sequenceFlows);
            if (Objects.nonNull(identifier)) {
                incoming.add(identifier);
            }
        }
        //当前网关节点的子节点（整个并行网关的子节点）
        JSONObject childNode = flowNode.getJSONObject("childNode");
        if (Objects.nonNull(childNode)) {
            // 普通结束网关
            //有并行分支但无条件节点对应的子节点
            if (CollectionUtils.isEmpty(incoming)) {
                return create(parallelGatewayId, childNode, model, process, sequenceFlows);
            } else {
                // 所有 service task 连接 end parallel gateway
                //并行分支汇合到下面的childNode节点
                childNode.put("incoming", incoming);
                FlowElement flowElement = model.getFlowElement(incoming.get(0));
                // 1.0 先进行线连接, 暂存 nextNode
                JSONObject nextNode = childNode.getJSONObject("childNode");
                childNode.put("childNode", null);
                String identifier = create(incoming.get(0), childNode, model, process, sequenceFlows);
                //其他并行分支和第一个并行分支连线的终点节点相同
                for (int i = 1; i < incoming.size(); i++) {
                    FlowElement flowElement1 = model.getFlowElement(incoming.get(i));
                    process.addFlowElement(connect(flowElement1.getId(), identifier, sequenceFlows));
                }
                // 1.1 线连接完成后，再进行 nextNode 创建
                if (Objects.nonNull(nextNode)) {
                    return create(identifier, nextNode, model, process, sequenceFlows);
                } else {
                    return identifier;
                }
            }
        }
        return parallelGatewayId;
    }

    //流转到服务任务(serviceTask)节点时会自动执行某些操作然后向下流转
    private static String createServiceTask(JSONObject flowNode, Process process, List<SequenceFlow> sequenceFlows) {
        List<String> incoming = flowNode.getJSONArray("incoming").toJavaList(String.class);
        // 自动生成id
        String id = id("serviceTask");
        if (incoming != null && !incoming.isEmpty()) {
            ServiceTask serviceTask = new ServiceTask();
            serviceTask.setName(flowNode.getString("nodeName"));
            serviceTask.setId(id);
            process.addFlowElement(serviceTask);
            // TODO: 2023/2/16 抄送实现，需求可不做
//            serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_CLASS);
//            serviceTask.setImplementation("实现JavaDelegate接口的类路径");
            process.addFlowElement(connect(incoming.get(0), id, sequenceFlows));
        }
        return id;
    }

    private static String createTask(JSONObject flowNode, Process process, List<SequenceFlow> sequenceFlows) {
        List<String> incoming = null;
        if(!Objects.isNull(flowNode.getJSONArray("incoming"))){
            incoming = flowNode.getJSONArray("incoming").toJavaList(String.class);
        }
        String id = "";
        if (incoming != null && !incoming.isEmpty()) {
            UserTask userTask = new UserTask();
            userTask.setName(flowNode.getString("nodeName"));

            //设置监听器
            ArrayList<FlowableListener> taskListeners = new ArrayList<>();
            FlowableListener taskListener = new FlowableListener();
            // 事件类型
            taskListener.setEvent(TaskListener.EVENTNAME_CREATE);
            // 监听器类型
            taskListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
            // 设置实现了，这里设置监听器的类型是delegateExpression，这样可以在实现类注入Spring bean.
            taskListener.setImplementation("${taskCreatedListener}");
            taskListeners.add(taskListener);
            userTask.setTaskListeners(taskListeners);
            //根节点
            if (Type.ROOT.type.equals(flowNode.getInteger("type"))) {
                //设置root的id保证TaskCreatedListener的第一个if条件完成，从而能执行CounterSignListener
                id = id("root");
            }
            //用户审批任务节点
            else {
                id = id("userTask");
                //设置监听器
                ArrayList<FlowableListener> listeners = new ArrayList<>();
                FlowableListener flowableListener = new FlowableListener();
                // 事件类型
                flowableListener.setEvent(ExecutionListener.EVENTNAME_START);
                // 监听器类型
                flowableListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
                // 设置实现了，这里设置监听器的类型是delegateExpression，这样可以在实现类注入Spring bean.
                flowableListener.setImplementation("${counterSignListener}");
                listeners.add(flowableListener);
                userTask.setExecutionListeners(listeners);

                //多实例，在业务流程中，为特定步骤定义重复的方式
                MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
                String examineMode = flowNode.getString("examineMode");
                // 审批人集合参数，会签集合变量名称,flowable:collection=“id+assigneeList”
                //collection 赋值使用原因:
                //      通过上面的配置，当指定了 collection 的流程变量后，在引擎自动生成多任务时每个任务的assign都有值了。
                //      就不再需要增加业务逻辑处理(遍历多任务后然后拿到审批人，依次给每个任务塞 处理人)
                multiInstanceLoopCharacteristics.setInputDataItem(id + "assigneeList");
                // 迭代集合，会签集合遍历名称(集合每个元素的标识)
                multiInstanceLoopCharacteristics.setElementVariable("assigneeName");
                // 并行，配置会签节点人员办理顺序 串行:true 并行:false
                multiInstanceLoopCharacteristics.setSequential(false);
                // 设置下一节点处理人表达式,flowable:assignee=“${assigneeName}”(上面集合每个元素的标识的实际使用)
                userTask.setAssignee("${assigneeName}");
                // 设置多实例属性，多实例：会签情况下每个处理人对应一个实例
                userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
                //多人审批时采用的审批方式 1依次会签 2并行会签 3或签
                //默认并行会签，会签的实例存ACT_RU_IDENTITYLINK表，串行时表中会依次增加实例数据
                if ("1".equals(examineMode)) {
                    multiInstanceLoopCharacteristics.setSequential(true);
                } else if ("3".equals(examineMode)) {
                    //或签，即设置完成条件nrOfCompletedInstances/nrOfInstances > 0
                    //nrOfCompletedInstances:已完成的实例数量，nrOfInstances:实例总数
                    multiInstanceLoopCharacteristics.setCompletionCondition("${nrOfCompletedInstances/nrOfInstances > 0}");
                }
            }
            flowNode.put("id", id);
            userTask.setId(id);
            process.addFlowElement(userTask);
            process.addFlowElement(connect(incoming.get(0), id, sequenceFlows));
        }
        return id;
    }

    private enum Type {
        //0 发起人 1审批任务 2抄送任务 4条件路由 5并行网关
        ROOT(0, UserTask.class),
        USER_TASK(1, UserTask.class),
        CC(2, ServiceTask.class),
        ROUTER(4, ExclusiveGateway.class),
        PARALLEL(5, ParallelGateway.class),
        SERVER_TASK(6, UserTask.class);

        private Integer type;
        private Class<?> typeClass;

        Type(Integer type, Class<?> typeClass) {
            this.type = type;
            this.typeClass = typeClass;
        }

        public final static Map<Integer, Class<?>> TYPE_MAP = Maps.newHashMap();

        static {
            for (Type element : Type.values()) {
                TYPE_MAP.put(element.type, element.typeClass);
            }
        }

        public boolean isEqual(String type) {
            return this.type.equals(type);
        }
    }


    private static String id(String prefix) {
        return prefix + "_" + UUID.randomUUID().toString().replace("-", "").toLowerCase();
    }

    private static ServiceTask serviceTask(String name) {
        ServiceTask serviceTask = new ServiceTask();
        serviceTask.setName(name);
        return serviceTask;
    }

    /**
     * 新建连线实体加入连线列表
     */
    protected static SequenceFlow connect(String from, String to, List<SequenceFlow> sequenceFlows) {
        SequenceFlow flow = new SequenceFlow();
        flow.setId(id("sequenceFlow"));
        flow.setSourceRef(from);
        flow.setTargetRef(to);
        sequenceFlows.add(flow);
        return flow;
    }

    protected static StartEvent createStartEvent() {
        StartEvent startEvent = new StartEvent();
        startEvent.setId(id("start"));
        startEvent.setInitiator("applyUserId");
        return startEvent;
    }

    protected static EndEvent createEndEvent() {
        EndEvent endEvent = new EndEvent();
        endEvent.setId(id("end"));
        // 事件结束监听器
        ArrayList<FlowableListener> listeners = new ArrayList<>();
        FlowableListener listener = new FlowableListener();
        // 结束节点开始时
        listener.setEvent((ExecutionListener.EVENTNAME_START));
        // 监听器类型
        listener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
        listener.setImplementation("${processUpdateStateListener}");
        listeners.add(listener);
        endEvent.setExecutionListeners(listeners);
        return endEvent;
    }

    public static ExclusiveGateway createExclusiveGateWayEnd(String id) {
        ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
        exclusiveGateway.setId(id);
        return exclusiveGateway;
    }

    private static ParallelGateway createParallelGateWayEnd(String id) {
        ParallelGateway parallelGateway = new ParallelGateway();
        parallelGateway.setId(id);
        return parallelGateway;
    }
}
