package com.xjrsoft.module.workflow.service.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Session;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.yulichang.toolkit.MPJWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.enums.EnabledMark;
import com.xjrsoft.common.enums.FormTemplateType;
import com.xjrsoft.common.enums.WorkflowApproveType;
import com.xjrsoft.common.enums.WorkflowAutoAgreeType;
import com.xjrsoft.common.enums.WorkflowIsPrevChooseNextType;
import com.xjrsoft.common.enums.WorkflowIsRecycleType;
import com.xjrsoft.common.enums.WorkflowMultiInstanceFinishType;
import com.xjrsoft.common.enums.WorkflowMultiInstanceType;
import com.xjrsoft.common.enums.WorkflowNoHandlerType;
import com.xjrsoft.common.enums.WorkflowRelationAuthType;
import com.xjrsoft.common.enums.YesOrNoEnum;
import com.xjrsoft.common.exception.MyException;
import com.xjrsoft.common.page.ConventPage;
import com.xjrsoft.common.page.PageOutput;
import com.xjrsoft.common.utils.RedisUtil;
import com.xjrsoft.common.utils.VoToColumnUtil;
import com.xjrsoft.module.form.dto.FormExecuteWorkflowAddDto;
import com.xjrsoft.module.form.dto.FormExecuteWorkflowUpdateDto;
import com.xjrsoft.module.form.entity.FormTemplate;
import com.xjrsoft.module.form.mapper.FormTemplateMapper;
import com.xjrsoft.module.form.service.IFormExecuteService;
import com.xjrsoft.module.magicapi.service.IMagicApiService;
import com.xjrsoft.module.magicapi.vo.MagicApiInfoVo;
import com.xjrsoft.module.organization.entity.User;
import com.xjrsoft.module.organization.entity.UserRoleRelation;
import com.xjrsoft.module.organization.service.IUserService;
import com.xjrsoft.module.system.entity.File;
import com.xjrsoft.module.system.entity.Stamp;
import com.xjrsoft.module.system.service.IFileService;
import com.xjrsoft.module.system.service.IStampService;
import com.xjrsoft.module.workflow.constant.WorkflowConstant;
import com.xjrsoft.module.workflow.dto.AddOrSubSignDto;
import com.xjrsoft.module.workflow.dto.ApproveDto;
import com.xjrsoft.module.workflow.dto.ApproveMultiDto;
import com.xjrsoft.module.workflow.dto.ApproveMultiInfoDto;
import com.xjrsoft.module.workflow.dto.ApproveUserDto;
import com.xjrsoft.module.workflow.dto.ApproveUserMultiDto;
import com.xjrsoft.module.workflow.dto.CirculatedTaskPageDto;
import com.xjrsoft.module.workflow.dto.DeployDto;
import com.xjrsoft.module.workflow.dto.DraftPageDto;
import com.xjrsoft.module.workflow.dto.FinishedTaskPageDto;
import com.xjrsoft.module.workflow.dto.FormFinishedTaskDto;
import com.xjrsoft.module.workflow.dto.GetAssigneeDto;
import com.xjrsoft.module.workflow.dto.LaunchDto;
import com.xjrsoft.module.workflow.dto.LaunchRelationTaskDto;
import com.xjrsoft.module.workflow.dto.MonitorPageDto;
import com.xjrsoft.module.workflow.dto.MoveRecycleDto;
import com.xjrsoft.module.workflow.dto.MyExaminePageDto;
import com.xjrsoft.module.workflow.dto.MyProcessPageDto;
import com.xjrsoft.module.workflow.dto.PendingTaskPageDto;
import com.xjrsoft.module.workflow.dto.ReLaunchDto;
import com.xjrsoft.module.workflow.dto.RecycleDeleteDto;
import com.xjrsoft.module.workflow.dto.RecycleProcessPageDto;
import com.xjrsoft.module.workflow.dto.RejectNodeDto;
import com.xjrsoft.module.workflow.dto.RelationTaskInfoDto;
import com.xjrsoft.module.workflow.dto.RelationTaskPageDto;
import com.xjrsoft.module.workflow.dto.RestartDto;
import com.xjrsoft.module.workflow.dto.SaveDraftDto;
import com.xjrsoft.module.workflow.dto.SetAssigneeDto;
import com.xjrsoft.module.workflow.dto.SetSuspendedDto;
import com.xjrsoft.module.workflow.dto.TransferDto;
import com.xjrsoft.module.workflow.dto.UpdateDraftDto;
import com.xjrsoft.module.workflow.dto.WithdrawDto;
import com.xjrsoft.module.workflow.entity.WorkflowApproveRecord;
import com.xjrsoft.module.workflow.entity.WorkflowCirculated;
import com.xjrsoft.module.workflow.entity.WorkflowDelegate;
import com.xjrsoft.module.workflow.entity.WorkflowDraft;
import com.xjrsoft.module.workflow.entity.WorkflowExtra;
import com.xjrsoft.module.workflow.entity.WorkflowFormRelation;
import com.xjrsoft.module.workflow.entity.WorkflowRecord;
import com.xjrsoft.module.workflow.entity.WorkflowSchema;
import com.xjrsoft.module.workflow.mapper.WorkflowDelegateMapper;
import com.xjrsoft.module.workflow.mapper.WorkflowExtraMapper;
import com.xjrsoft.module.workflow.mapper.WorkflowRecordMapper;
import com.xjrsoft.module.workflow.mapper.WorkflowSchemaMapper;
import com.xjrsoft.module.workflow.mapper.XjrWorkflowExtraMapper;
import com.xjrsoft.module.workflow.model.ApiConfig;
import com.xjrsoft.module.workflow.model.ApiRequestParamsConfig;
import com.xjrsoft.module.workflow.model.AuthConfig;
import com.xjrsoft.module.workflow.model.ButtonConfig;
import com.xjrsoft.module.workflow.model.FormAssignmentConfig;
import com.xjrsoft.module.workflow.model.FormConfig;
import com.xjrsoft.module.workflow.model.MemberConfig;
import com.xjrsoft.module.workflow.model.ProcessParamConfig;
import com.xjrsoft.module.workflow.model.RelationProcessConfig;
import com.xjrsoft.module.workflow.model.StartNodeConfig;
import com.xjrsoft.module.workflow.model.UserTaskConfig;
import com.xjrsoft.module.workflow.model.WorkflowSchemaConfig;
import com.xjrsoft.module.workflow.service.IWorkflowApproveRecordService;
import com.xjrsoft.module.workflow.service.IWorkflowCirculatedService;
import com.xjrsoft.module.workflow.service.IWorkflowDraftService;
import com.xjrsoft.module.workflow.service.IWorkflowExecuteService;
import com.xjrsoft.module.workflow.service.IWorkflowExtraService;
import com.xjrsoft.module.workflow.service.IWorkflowFormRelationService;
import com.xjrsoft.module.workflow.service.IWorkflowRecordService;
import com.xjrsoft.module.workflow.utils.WorkFlowUtil;
import com.xjrsoft.module.workflow.vo.AllRecordListVo;
import com.xjrsoft.module.workflow.vo.ApproveMultiInfoVo;
import com.xjrsoft.module.workflow.vo.ApproveMultiVo;
import com.xjrsoft.module.workflow.vo.CirculatedTaskPageVo;
import com.xjrsoft.module.workflow.vo.DraftInfoVo;
import com.xjrsoft.module.workflow.vo.DraftPageVo;
import com.xjrsoft.module.workflow.vo.FinishedTaskPageVo;
import com.xjrsoft.module.workflow.vo.FinishedTaskVo;
import com.xjrsoft.module.workflow.vo.FormFinishedTaskVo;
import com.xjrsoft.module.workflow.vo.GetAssigneeVo;
import com.xjrsoft.module.workflow.vo.GetCountVo;
import com.xjrsoft.module.workflow.vo.HistoryTaskVo;
import com.xjrsoft.module.workflow.vo.LaunchAndApproveVo;
import com.xjrsoft.module.workflow.vo.MonitorPageVo;
import com.xjrsoft.module.workflow.vo.MyProcessPageVo;
import com.xjrsoft.module.workflow.vo.PendingTaskVo;
import com.xjrsoft.module.workflow.vo.ProcessInfoVo;
import com.xjrsoft.module.workflow.vo.ProcessRecordListVo;
import com.xjrsoft.module.workflow.vo.RecycleProcessInfoVo;
import com.xjrsoft.module.workflow.vo.RecycleProcessPageVo;
import com.xjrsoft.module.workflow.vo.RejectNodeVo;
import com.xjrsoft.module.workflow.vo.RelationFormInfoVo;
import com.xjrsoft.module.workflow.vo.RelationTaskInfoVo;
import com.xjrsoft.module.workflow.vo.RelationTaskPageVo;
import com.xjrsoft.module.workflow.vo.RestartVo;
import com.xjrsoft.module.workflow.vo.StartNodeFormInfoVo;
import com.xjrsoft.module.workflow.vo.StartProcessInfoVo;
import com.xjrsoft.module.workflow.vo.StartProcessRelationTaskVo;
import com.xjrsoft.module.workflow.vo.TaskInfoRelationTaskVo;
import com.xjrsoft.module.workflow.vo.TaskInfoVo;
import com.xjrsoft.module.workflow.vo.UserTaskFormInfoVo;
import com.xjrsoft.module.workflow.vo.UserTaskInfoVo;
import com.xjrsoft.module.workflow.vo.UserTaskRelationTaskVo;
import com.xjrsoft.module.workflow.vo.WorkflowSchemaInfoVo;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Triple;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.camunda.bpm.engine.history.HistoricProcessInstanceQuery;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.history.HistoricTaskInstanceQuery;
import org.camunda.bpm.engine.history.HistoricVariableInstance;
import org.camunda.bpm.engine.impl.persistence.entity.HistoricVariableInstanceEntity;
import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.runtime.ActivityInstance;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.runtime.VariableInstance;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.engine.task.TaskQuery;
import org.camunda.bpm.engine.variable.VariableMap;
import org.camunda.bpm.engine.variable.Variables;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.impl.instance.FlowNodeImpl;
import org.camunda.bpm.model.bpmn.instance.SequenceFlow;
import org.camunda.bpm.model.xml.instance.ModelElementInstance;
import org.camunda.commons.utils.IoUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.ssssssss.magicapi.core.service.MagicAPIService;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Author: tzx
 * @Date: 2022/9/8 14:25
 */
@Slf4j
@Service
@AllArgsConstructor
public class WorkflowExecuteServiceImpl implements IWorkflowExecuteService {

    private final WorkflowSchemaMapper workflowSchemaMapper;

    private final RuntimeService runtimeService;

    private final RepositoryService repositoryService;

    private final TaskService taskService;

    private final IFormExecuteService formExecuteService;

    private final RedisUtil redisUtil;

    private final WorkflowExtraMapper workflowExtraMapper;

    private final HistoryService historyService;

    private final FormTemplateMapper formTemplateMapper;

    private final IWorkflowDraftService workflowDraftService;

    private final WorkflowDelegateMapper workflowDelegateMapper;

    private final WorkflowRecordMapper workflowRecordMapper;

    private final IWorkflowCirculatedService circulatedService;

    private final IWorkflowApproveRecordService approveRecordService;

    private final IStampService stampService;

    private final IWorkflowExtraService extraService;


    private final IFileService fileService;

    private final IWorkflowFormRelationService formRelationService;

    private final IUserService userService;

    private final ObjectMapper objectMapper;

    private final XjrWorkflowExtraMapper xjrWorkflowExtraMapper;
    /**
     * 数据库存储xml字段的后缀
     */
    private static final String DB_FIELD_XML_PREFIX = "xml";


    @Override
    public boolean deploy(DeployDto dto) {
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectById(dto.getSchemaId());

        Deployment deploy = repositoryService.createDeployment().name(workflowSchema.getName()).addInputStream(workflowSchema.getName() + StringPool.DOT + WorkflowConstant.WORKFLOW_SUFFIX, IoUtil.stringAsInputStream(workflowSchema.getXmlContent())).deploy();
        workflowSchema.setDeploymentId(deploy.getId());

        return workflowSchemaMapper.updateById(workflowSchema) > 0;
    }

    @Override
    public String preview(Long schemaId) {

        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, schemaId).select(x -> x.getProperty().startsWith(DB_FIELD_XML_PREFIX)));

        return workflowSchema.getXmlContent();
    }

    @Override
    public StartProcessInfoVo getStartProcessInfo(Long schemaId) {

        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, schemaId));

        StartProcessInfoVo vo = new StartProcessInfoVo();

        vo.setWorkflowChat(workflowSchema.getWorkflowChat());

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);
        //流程参数  发布时  存到变量中

        List<RelationProcessConfig> relationProcessConfig = workflowSchemaConfig.getProcessConfig().getRelationProcessConfigs();

        List<Long> relationSchemaIds = relationProcessConfig.stream().map(RelationProcessConfig::getId).collect(Collectors.toList());

        if (relationSchemaIds.size() > 0) {
            LambdaQueryWrapper<WorkflowSchema> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(WorkflowSchema::getId, relationSchemaIds);
            queryWrapper.select(WorkflowSchema::getId, WorkflowSchema::getName);
            List<WorkflowSchema> workflowSchemas = workflowSchemaMapper.selectList(queryWrapper);

            List<StartProcessRelationTaskVo> relationTasks = new ArrayList<>(workflowSchemas.size());
            for (WorkflowSchema schema : workflowSchemas) {
                StartProcessRelationTaskVo relationTaskVo = new StartProcessRelationTaskVo();
                relationTaskVo.setSchemaId(schema.getId());
                relationTaskVo.setSchemaName(schema.getName());
                relationTasks.add(relationTaskVo);
            }
            vo.setRelationTasks(relationTasks);
        }


        Optional<Map<String, Object>> startNodeConfigMap = workflowSchemaConfig.getChildNodeConfig().stream().filter(c -> c.containsValue(WorkflowConstant.START_NODE_DEFAULT_ID)).findFirst();

        if (!startNodeConfigMap.isPresent()) {
            throw new MyException("找不到当前流程的开始节点配置！");
        }

        //将map 转为 java类
        StartNodeConfig startNodeConfig = Convert.convert(StartNodeConfig.class, startNodeConfigMap.get());

        List<Long> formIds = startNodeConfig.getFormConfigs().stream().map(FormConfig::getFormId).collect(Collectors.toList());
        List<FormTemplate> formTemplates = formTemplateMapper.selectList(Wrappers.lambdaQuery(FormTemplate.class).in(formIds.size() > 0, FormTemplate::getId, formIds));

        List<StartNodeFormInfoVo> formInfoVos = new ArrayList<>(startNodeConfig.getFormConfigs().size());


        for (FormConfig formConfig : startNodeConfig.getFormConfigs()) {

            StartNodeFormInfoVo formInfoVo = new StartNodeFormInfoVo();
            formInfoVo.setFormType(formConfig.getFormType());
            formInfoVo.setFormConfig(formConfig);

            //TODO 系统表单 和 自定义表单 走同一套流程  都是使用json  以后看能否有更好的解决方案 解决用户手动修改系统表单代码
            if (formConfig.getFormType() == FormTemplateType.CUSTOM.getCode() || formConfig.getFormType() == FormTemplateType.SYSTEM.getCode()) {
                Optional<FormTemplate> templateOp = formTemplates.stream().filter(f -> Objects.equals(f.getId(), formConfig.getFormId())).findFirst();
                templateOp.ifPresent(t -> formInfoVo.setFormJson(t.getFormJson()));
            }

            formInfoVos.add(formInfoVo);
        }
        vo.setFormInfos(formInfoVos);
        vo.setSchemaInfo(BeanUtil.toBean(workflowSchema, WorkflowSchemaInfoVo.class));

        //获取流程数据
        List<FormAssignmentConfig> formAssignmentConfigs = startNodeConfig.getAssignmentConfig().getFormAssignmentConfigs();

        //是否有表单赋值
        if (formAssignmentConfigs.size() > 0) {

            List<ProcessParamConfig> processParamConfigs = workflowSchemaConfig.getProcessConfig().getProcessParamConfigs();

            long count = historyService.createHistoricProcessInstanceQuery().count();
            Map<String, Object> processParam = WorkFlowUtil.getProcessParam(processParamConfigs, workflowSchema, count + 1, null);

            Map<String, Map<String, Object>> formAssignmentData = new HashMap<>();

            for (FormAssignmentConfig formAssignmentConfig : formAssignmentConfigs) {

                if (formAssignmentData.containsKey(formAssignmentConfig.getTarget().getFormId())) {
                    Map<String, Object> thisFormAssignmentData = formAssignmentData.get(formAssignmentConfig.getTarget().getFormId());
                    thisFormAssignmentData.put(formAssignmentConfig.getTarget().getFormField(), processParam.get(formAssignmentConfig.getSource()));
                } else {
                    Map<String, Object> thisFormAssignmentData = new HashMap<>();
                    thisFormAssignmentData.put(formAssignmentConfig.getTarget().getFormField(), processParam.get(formAssignmentConfig.getSource()));
                    formAssignmentData.put(formAssignmentConfig.getTarget().getFormId(), thisFormAssignmentData);
                }

            }

            vo.setFormAssignmentData(formAssignmentData);

        }


        return vo;
    }


    @Override
    public UserTaskInfoVo getApproveProcessInfo(String taskId) {

        HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new MyException("找不到该流程！");
        }
        List<HistoricVariableInstance> variableInstanceList = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .variableNameIn(WorkflowConstant.PROCESS_SCHEMA_ID_KEY, WorkflowConstant.RELATION_TASK_KEY)
                .list();

        Optional<HistoricVariableInstance> schemaIdVar = variableInstanceList.stream().filter(x -> x.getName().equals(WorkflowConstant.PROCESS_SCHEMA_ID_KEY)).findFirst();

        if (!schemaIdVar.isPresent()) {
            throw new MyException("找不到此模板！");
        }

        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, schemaIdVar.get().getValue()));

        UserTaskInfoVo vo = new UserTaskInfoVo();
        vo.setWorkflowChat(workflowSchema.getWorkflowChat());

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);

        Optional<HistoricVariableInstance> relationTasksOp = variableInstanceList.stream().filter(x -> x.getName().equals(WorkflowConstant.RELATION_TASK_KEY)).findFirst();

        //如果关联任务不为空
        if (relationTasksOp.isPresent()) {
            List<LaunchRelationTaskDto> relationTasks = Convert.toList(LaunchRelationTaskDto.class, relationTasksOp.get().getValue());

            if (relationTasks != null && relationTasks.size() > 0) {

                List<Long> relationSchemaIds = relationTasks.stream().map(LaunchRelationTaskDto::getSchemaId).collect(Collectors.toList());

                LambdaQueryWrapper<WorkflowSchema> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.in(WorkflowSchema::getId, relationSchemaIds);
                queryWrapper.select(WorkflowSchema::getId, WorkflowSchema::getName);
                List<WorkflowSchema> workflowSchemas = workflowSchemaMapper.selectList(queryWrapper);
                List<UserTaskRelationTaskVo> relationTasksVo = new ArrayList<>(workflowSchemas.size());

                for (LaunchRelationTaskDto relationTask : relationTasks) {
                    UserTaskRelationTaskVo relationTaskVo = new UserTaskRelationTaskVo();
                    relationTaskVo.setTaskId(relationTask.getTaskId());
                    relationTaskVo.setSchemaId(relationTask.getSchemaId());
                    Optional<WorkflowSchema> thisSchema = workflowSchemas.stream().filter(schema -> schema.getId().equals(relationTask.getSchemaId())).findFirst();
                    thisSchema.ifPresent(schema -> {
                        relationTaskVo.setSchemaName(schema.getName());
                    });
                    relationTasksVo.add(relationTaskVo);
                }
                vo.setRelationTasks(relationTasksVo);
            }

        }

        Optional<Map<String, Object>> taskNodeConfigMap = workflowSchemaConfig.getChildNodeConfig().stream().filter(c -> c.containsValue(task.getTaskDefinitionKey())).findFirst();

        if (!taskNodeConfigMap.isPresent()) {
            throw new MyException("找不到当前节点的配置信息！");
        }

        UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, taskNodeConfigMap.get());

        List<Long> formIds = userTaskConfig.getFormConfigs().stream().map(FormConfig::getFormId).collect(Collectors.toList());
        String[] formKeys = userTaskConfig.getFormConfigs().stream().map(FormConfig::getKey).toArray(String[]::new);
        List<FormTemplate> formTemplates = formTemplateMapper.selectList(Wrappers.lambdaQuery(FormTemplate.class).in(formIds.size() > 0, FormTemplate::getId, formIds));

        List<HistoricVariableInstance> allFormData = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .variableNameIn(formKeys)
                .list();


        List<UserTaskFormInfoVo> formInfoVos = new ArrayList<>(userTaskConfig.getFormConfigs().size());
        for (FormConfig formConfig : userTaskConfig.getFormConfigs()) {

            UserTaskFormInfoVo formInfoVo = new UserTaskFormInfoVo();
            formInfoVo.setFormType(formConfig.getFormType());
            formInfoVo.setFormConfig(formConfig);

            if (allFormData.stream().anyMatch(x -> x.getName().equals(formConfig.getKey()))) {
                HistoricVariableInstance historicVariableInstance = allFormData.stream().filter(x -> x.getName().equals(formConfig.getKey())).findFirst().orElse(new HistoricVariableInstanceEntity());
                formInfoVo.setFormData(Convert.toMap(String.class, Object.class, historicVariableInstance.getValue()));
            }

            //TODO 系统表单 和 自定义表单 走同一套流程  都是使用json  以后看能否有更好的解决方案 解决用户手动修改系统表单代码
            if (formConfig.getFormType() == FormTemplateType.CUSTOM.getCode() || formConfig.getFormType() == FormTemplateType.SYSTEM.getCode()) {
                Optional<FormTemplate> templateOp = formTemplates.stream().filter(f -> Objects.equals(f.getId(), formConfig.getFormId())).findFirst();
                templateOp.ifPresent(t -> formInfoVo.setFormJson(t.getFormJson()));
            }

            formInfoVos.add(formInfoVo);
        }
        vo.setFormInfos(formInfoVos);
        vo.setSchemaInfo(BeanUtil.toBean(workflowSchema, WorkflowSchemaInfoVo.class));
        vo.setOpinionConfig(userTaskConfig.getOpinionConfig());

        //如果是多实例的
        if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() != WorkflowMultiInstanceType.NONE.getCode()) {
            vo.setIsAddOrSubSign(userTaskConfig.getCountersignConfig().getAddOrRemove());
            //如果不是none  就代表是会签节点
            vo.setIsCountersign(Boolean.TRUE);
            vo.setButtonConfigs(userTaskConfig.getButtonConfigs().stream().filter(ButtonConfig::getChecked).filter(buttonConfig -> buttonConfig.getApproveType() == WorkflowApproveType.AGREE.getCode() || buttonConfig.getApproveType() == WorkflowApproveType.DISAGREE.getCode()).collect(Collectors.toList()));

        } else {
            vo.setIsCountersign(Boolean.FALSE);
            vo.setButtonConfigs(userTaskConfig.getButtonConfigs().stream().filter(ButtonConfig::getChecked).collect(Collectors.toList()));

        }

        if (userTaskConfig.getOpinionConfig().getEnabled()) {
            MPJLambdaWrapper<WorkflowApproveRecord> workflowApproveRecordMPJLambdaWrapper = MPJWrappers.<WorkflowApproveRecord>lambdaJoin()
                    .disableSubLogicDel()
                    .eq(WorkflowApproveRecord::getProcessId, task.getProcessInstanceId())
                    .select(WorkflowApproveRecord::getId)
                    .select(WorkflowApproveRecord.class, x -> VoToColumnUtil.fieldsToColumns(WorkflowApproveRecord.class).contains(x.getProperty()))
                    .selectAs(User::getName, WorkflowApproveRecord::getApproveUserName)
                    .leftJoin(User.class, User::getId, WorkflowApproveRecord::getApproveUserId)
                    .selectAs(Stamp::getFileUrl, WorkflowApproveRecord::getApproveStampUrl)
                    .leftJoin(Stamp.class, Stamp::getId, WorkflowApproveRecord::getApproveStamp);

            List<WorkflowApproveRecord> approveRecords = approveRecordService.selectJoinList(WorkflowApproveRecord.class, workflowApproveRecordMPJLambdaWrapper);

            //取时间最大值 不好取  目前还是全部查出来 然后根据时间 倒序 取第一条数据
            if (userTaskConfig.getOpinionConfig().getShowType() == 1 && approveRecords.size() > 0) {
                WorkflowApproveRecord approveRecord = approveRecords.stream().sorted(Comparator.comparing(WorkflowApproveRecord::getApproveTime).reversed()).collect(Collectors.toList()).get(0);
                List<WorkflowApproveRecord> onlyOne = new ArrayList<>();
                onlyOne.add(approveRecord);
                vo.setTaskApproveOpinions(onlyOne);
            } else {
                vo.setTaskApproveOpinions(approveRecords);
            }
        }

        //获取流程数据
        List<FormAssignmentConfig> formAssignmentConfigs = userTaskConfig.getAssignmentConfig().getFormAssignmentConfigs();

        long taskSize = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).count();
        //是否有表单赋值
        if (formAssignmentConfigs.size() > 0 && taskSize > 0) {//有任务才给表单进行赋值，防止结束之后点击查看时获取不到对应的流程参数。

            Object variable = taskService.getVariable(taskId, WorkflowConstant.PROCESS_PARAM_KEY);
            Map<String, Object> processParam = Convert.toMap(String.class, Object.class, variable);

            Map<String, Map<String, Object>> formAssignmentData = new HashMap<>();

            for (FormAssignmentConfig formAssignmentConfig : formAssignmentConfigs) {

                if (formAssignmentData.containsKey(formAssignmentConfig.getTarget().getFormId())) {
                    Map<String, Object> thisFormAssignmentData = formAssignmentData.get(formAssignmentConfig.getTarget().getFormId());
                    thisFormAssignmentData.put(formAssignmentConfig.getTarget().getFormField(), processParam.get(formAssignmentConfig.getSource()));
                } else {
                    Map<String, Object> thisFormAssignmentData = new HashMap<>();
                    thisFormAssignmentData.put(formAssignmentConfig.getTarget().getFormField(), processParam.get(formAssignmentConfig.getSource()));
                    formAssignmentData.put(formAssignmentConfig.getTarget().getFormId(), thisFormAssignmentData);
                }

            }

            vo.setFormAssignmentData(formAssignmentData);

        }

        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();

        String superProcessInstanceId = historicProcessInstance.getSuperProcessInstanceId();

        List<Map<String, Object>> otherProcessApproveRecord = new ArrayList<>();

        //此流程 是否有主流程  如果有 代表他是某个流程的子流程
        if (StrUtil.isNotBlank(superProcessInstanceId)) {

            HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(superProcessInstanceId).variableName(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY).singleResult();
            //获取主流程审批记录
            List<ProcessRecordListVo> processRecordListVos = getProcessRecordListVos(superProcessInstanceId, 0);

            Map<String, Object> superProcessMap = new HashMap<>();
            superProcessMap.put("schemaName", historicVariableInstance.getValue() + "(上级流程)");
            superProcessMap.put("records", processRecordListVos);

            otherProcessApproveRecord.add(superProcessMap);

        }

        //查看当前流程 是否包含子流程
        List<HistoricProcessInstance> childProcess = historyService.createHistoricProcessInstanceQuery().superProcessInstanceId(task.getProcessInstanceId()).list();

        if (childProcess.size() > 0) {
            String[] strings = childProcess.stream().map(HistoricProcessInstance::getId).toArray(String[]::new);

            List<HistoricVariableInstance> childSchemaNameVarList = historyService.createHistoricVariableInstanceQuery().processInstanceIdIn(strings).variableName(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY).list();

            for (HistoricProcessInstance process : childProcess) {
                Optional<HistoricVariableInstance> varOp = childSchemaNameVarList.stream().filter(x -> x.getProcessInstanceId().equals(process.getId())).findFirst();

                varOp.ifPresent(schemaNameVar -> {
                    Map<String, Object> superProcessMap = new HashMap<>();
                    superProcessMap.put("schemaName", schemaNameVar.getValue() + "(下级流程)");

                    //获取主流程审批记录
                    List<ProcessRecordListVo> processRecordListVos = getProcessRecordListVos(schemaNameVar.getProcessInstanceId(), 0);
                    superProcessMap.put("records", processRecordListVos);

                    otherProcessApproveRecord.add(superProcessMap);
                });
            }
        }
        //TODO  新增返回值  返回 主/子 流程 审批记录

        List<ProcessRecordListVo> recordListVos = getProcessRecordListVos(task.getProcessInstanceId(), 0);
        vo.setTaskRecords(recordListVos);
        vo.setOtherProcessApproveRecord(otherProcessApproveRecord);
        return vo;

    }


    @Override
    public UserTaskInfoVo getApproveProcessInfoByProcessId(String processId) {

        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult();

        List<HistoricVariableInstance> variableInstanceList = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(processId)
                .variableNameIn(WorkflowConstant.PROCESS_SCHEMA_ID_KEY, WorkflowConstant.RELATION_TASK_KEY)
                .list();

        Optional<HistoricVariableInstance> schemaIdVar = variableInstanceList.stream().filter(x -> x.getName().equals(WorkflowConstant.PROCESS_SCHEMA_ID_KEY)).findFirst();


        if (!schemaIdVar.isPresent()) {
            throw new MyException("找不到此模板！");
        }

        //查询当前流程是否包含父亲流程
        String superProcessInstanceId = historicProcessInstance.getSuperProcessInstanceId();

        //查看当前流程 是否包含子流程
        List<HistoricProcessInstance> childProcess = historyService.createHistoricProcessInstanceQuery().superProcessInstanceId(processId).list();

        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, schemaIdVar.get().getValue()));

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);

        UserTaskInfoVo vo = new UserTaskInfoVo();
        vo.setWorkflowChat(workflowSchema.getWorkflowChat());
        //如果 所有 节点 包含 用户任务 || 外部流程 ||子流程
        if (workflowSchemaConfig.getChildNodeConfig().stream().anyMatch(x -> x.containsValue(WorkflowConstant.BPMN_XML_USER_TASK_TYPE_NAME) || x.containsValue(WorkflowConstant.BPMN_XML_CALL_ACTIVITY_TYPE_NAME) || x.containsValue(WorkflowConstant.BPMN_XML_SUB_PROCESS_TYPE_NAME))) {
//            ActivityInstance activityInstance = runtimeService.getActivityInstance(processId);
//
//
//            //如果没有使用processId 查询 基本可以代表 当前节点 不是用户任务节点  是外部任务 子任务节点
//            ActivityInstance childActivityInstance = activityInstance.getChildActivityInstances()[0];


            List<WorkflowExtra> workflowExtras = workflowExtraMapper.selectList(Wrappers.lambdaQuery(WorkflowExtra.class).in(WorkflowExtra::getProcessId, processId));

            //找到当前流程的 最新的一个用户任务数据
            Optional<WorkflowExtra> lastUserTaskOp = workflowExtras.stream()
                    .filter(e -> e.getProcessId().equals(processId) && StrUtil.isNotBlank(e.getTaskId()))
                    .max(Comparator.comparing(WorkflowExtra::getStartTime));

            //如果不为空 能找到  就使用这个用户任务 查看流程
            if (lastUserTaskOp.isPresent()) {

                WorkflowExtra workflowExtra = lastUserTaskOp.get();

                Optional<HistoricVariableInstance> relationTasksOp = variableInstanceList.stream().filter(x -> x.getName().equals(WorkflowConstant.RELATION_TASK_KEY)).findFirst();

                //如果关联任务不为空
                if (relationTasksOp.isPresent()) {
                    List<LaunchRelationTaskDto> relationTasks = Convert.toList(LaunchRelationTaskDto.class, relationTasksOp.get().getValue());

                    if (relationTasks != null && relationTasks.size() > 0) {

                        List<Long> relationSchemaIds = relationTasks.stream().map(LaunchRelationTaskDto::getSchemaId).collect(Collectors.toList());

                        LambdaQueryWrapper<WorkflowSchema> queryWrapper = new LambdaQueryWrapper<>();
                        queryWrapper.in(WorkflowSchema::getId, relationSchemaIds);
                        queryWrapper.select(WorkflowSchema::getId, WorkflowSchema::getName);
                        List<WorkflowSchema> workflowSchemas = workflowSchemaMapper.selectList(queryWrapper);
                        List<UserTaskRelationTaskVo> relationTasksVo = new ArrayList<>(workflowSchemas.size());

                        for (LaunchRelationTaskDto relationTask : relationTasks) {
                            UserTaskRelationTaskVo relationTaskVo = new UserTaskRelationTaskVo();
                            relationTaskVo.setTaskId(relationTask.getTaskId());
                            relationTaskVo.setSchemaId(relationTask.getSchemaId());
                            Optional<WorkflowSchema> thisSchema = workflowSchemas.stream().filter(schema -> schema.getId().equals(relationTask.getSchemaId())).findFirst();
                            thisSchema.ifPresent(schema -> {
                                relationTaskVo.setSchemaName(schema.getName());
                            });
                            relationTasksVo.add(relationTaskVo);
                        }
                        vo.setRelationTasks(relationTasksVo);
                    }

                }

                //如果有taskId，表明不是纯粹的脚本任务，走以下逻辑
                HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().taskId(workflowExtra.getTaskId()).singleResult();

                Optional<Map<String, Object>> taskNodeConfigMap = workflowSchemaConfig.getChildNodeConfig().stream().filter(c -> c.containsValue(task.getTaskDefinitionKey())).findFirst();

                if (!taskNodeConfigMap.isPresent()) {
                    throw new MyException("找不到当前节点的配置信息！");
                }

                UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, taskNodeConfigMap.get());

                List<Long> formIds = userTaskConfig.getFormConfigs().stream().map(FormConfig::getFormId).collect(Collectors.toList());
                String[] formKeys = userTaskConfig.getFormConfigs().stream().map(FormConfig::getKey).toArray(String[]::new);
                List<FormTemplate> formTemplates = formTemplateMapper.selectList(Wrappers.lambdaQuery(FormTemplate.class).in(formIds.size() > 0, FormTemplate::getId, formIds));

                List<HistoricVariableInstance> allFormData = historyService.createHistoricVariableInstanceQuery()
                        .processInstanceId(processId)
                        .variableNameIn(formKeys)
                        .list();


                List<UserTaskFormInfoVo> formInfoVos = new ArrayList<>(userTaskConfig.getFormConfigs().size());
                for (FormConfig formConfig : userTaskConfig.getFormConfigs()) {

                    UserTaskFormInfoVo formInfoVo = new UserTaskFormInfoVo();
                    formInfoVo.setFormType(formConfig.getFormType());
                    formInfoVo.setFormConfig(formConfig);

                    if (allFormData.stream().anyMatch(x -> x.getName().equals(formConfig.getKey()))) {
                        HistoricVariableInstance historicVariableInstance = allFormData.stream().filter(x -> x.getName().equals(formConfig.getKey())).findFirst().orElse(new HistoricVariableInstanceEntity());
                        formInfoVo.setFormData(Convert.toMap(String.class, Object.class, historicVariableInstance.getValue()));
                    }

                    //TODO 系统表单 和 自定义表单 走同一套流程  都是使用json  以后看能否有更好的解决方案 解决用户手动修改系统表单代码
                    if (formConfig.getFormType() == FormTemplateType.CUSTOM.getCode() || formConfig.getFormType() == FormTemplateType.SYSTEM.getCode()) {
                        Optional<FormTemplate> templateOp = formTemplates.stream().filter(f -> Objects.equals(f.getId(), formConfig.getFormId())).findFirst();
                        templateOp.ifPresent(t -> formInfoVo.setFormJson(t.getFormJson()));
                    }

                    formInfoVos.add(formInfoVo);
                }
                vo.setFormInfos(formInfoVos);
                vo.setSchemaInfo(BeanUtil.toBean(workflowSchema, WorkflowSchemaInfoVo.class));
                vo.setButtonConfigs(userTaskConfig.getButtonConfigs().stream().filter(ButtonConfig::getChecked).collect(Collectors.toList()));
                vo.setOpinionConfig(userTaskConfig.getOpinionConfig());

                //如果是多实例的
                if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() != WorkflowMultiInstanceType.NONE.getCode()) {
                    vo.setIsAddOrSubSign(userTaskConfig.getCountersignConfig().getAddOrRemove());
                }

                if (userTaskConfig.getOpinionConfig().getEnabled()) {
                    MPJLambdaWrapper<WorkflowApproveRecord> workflowApproveRecordMPJLambdaWrapper = MPJWrappers.<WorkflowApproveRecord>lambdaJoin()
                            .disableSubLogicDel()
                            .eq(WorkflowApproveRecord::getProcessId, processId)
                            .select(WorkflowApproveRecord::getId)
                            .select(WorkflowApproveRecord.class, x -> VoToColumnUtil.fieldsToColumns(WorkflowApproveRecord.class).contains(x.getProperty()))
                            .selectAs(User::getName, WorkflowApproveRecord::getApproveUserName)
                            .leftJoin(User.class, User::getId, WorkflowApproveRecord::getApproveUserId)
                            .selectAs(Stamp::getFileUrl, WorkflowApproveRecord::getApproveStampUrl)
                            .leftJoin(Stamp.class, Stamp::getId, WorkflowApproveRecord::getApproveStamp);

                    List<WorkflowApproveRecord> approveRecords = approveRecordService.selectJoinList(WorkflowApproveRecord.class, workflowApproveRecordMPJLambdaWrapper);

                    //取时间最大值 不好取  目前还是全部查出来 然后根据时间 倒序 取第一条数据
                    if (userTaskConfig.getOpinionConfig().getShowType() == 1 && approveRecords.size() > 0) {
                        WorkflowApproveRecord approveRecord = approveRecords.stream().sorted(Comparator.comparing(WorkflowApproveRecord::getApproveTime).reversed()).collect(Collectors.toList()).get(0);
                        List<WorkflowApproveRecord> onlyOne = new ArrayList<>();
                        onlyOne.add(approveRecord);
                        vo.setTaskApproveOpinions(onlyOne);
                    } else {
                        vo.setTaskApproveOpinions(approveRecords);
                    }
                }

                //获取流程数据
                List<FormAssignmentConfig> formAssignmentConfigs = userTaskConfig.getAssignmentConfig().getFormAssignmentConfigs();

                long taskSize = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).count();

                //是否有表单赋值
                if (formAssignmentConfigs.size() > 0 && taskSize > 0) {

                    Object variable = taskService.getVariable(workflowExtra.getTaskId(), WorkflowConstant.PROCESS_PARAM_KEY);
                    Map<String, Object> processParam = Convert.toMap(String.class, Object.class, variable);

                    Map<String, Map<String, Object>> formAssignmentData = new HashMap<>();

                    for (FormAssignmentConfig formAssignmentConfig : formAssignmentConfigs) {

                        if (formAssignmentData.containsKey(formAssignmentConfig.getTarget().getFormId())) {
                            Map<String, Object> thisFormAssignmentData = formAssignmentData.get(formAssignmentConfig.getTarget().getFormId());
                            thisFormAssignmentData.put(formAssignmentConfig.getTarget().getFormField(), processParam.get(formAssignmentConfig.getSource()));
                        } else {
                            Map<String, Object> thisFormAssignmentData = new HashMap<>();
                            thisFormAssignmentData.put(formAssignmentConfig.getTarget().getFormField(), processParam.get(formAssignmentConfig.getSource()));
                            formAssignmentData.put(formAssignmentConfig.getTarget().getFormId(), thisFormAssignmentData);
                        }

                    }

                    vo.setFormAssignmentData(formAssignmentData);

                }


                List<Map<String, Object>> otherProcessApproveRecord = new ArrayList<>();

                //此流程 是否有主流程  如果有 代表他是某个流程的子流程
                if (StrUtil.isNotBlank(superProcessInstanceId)) {

                    HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(superProcessInstanceId).variableName(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY).singleResult();
                    //获取主流程审批记录
                    List<ProcessRecordListVo> processRecordListVos = getProcessRecordListVos(superProcessInstanceId, 0);

                    Map<String, Object> superProcessMap = new HashMap<>();
                    superProcessMap.put("schemaName", historicVariableInstance.getValue() + "(上级流程)");
                    superProcessMap.put("records", processRecordListVos);

                    otherProcessApproveRecord.add(superProcessMap);

                }


                if (childProcess.size() > 0) {
                    String[] strings = childProcess.stream().map(HistoricProcessInstance::getId).toArray(String[]::new);

                    List<HistoricVariableInstance> childSchemaNameVarList = historyService.createHistoricVariableInstanceQuery().processInstanceIdIn(strings).variableName(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY).list();

                    for (HistoricProcessInstance process : childProcess) {
                        Optional<HistoricVariableInstance> varOp = childSchemaNameVarList.stream().filter(x -> x.getProcessInstanceId().equals(process.getId())).findFirst();

                        varOp.ifPresent(schemaNameVar -> {
                            Map<String, Object> superProcessMap = new HashMap<>();
                            superProcessMap.put("schemaName", schemaNameVar.getValue() + "(下级流程)");

                            //获取主流程审批记录
                            List<ProcessRecordListVo> processRecordListVos = getProcessRecordListVos(schemaNameVar.getProcessInstanceId(), 0);
                            superProcessMap.put("records", processRecordListVos);

                            otherProcessApproveRecord.add(superProcessMap);
                        });
                    }
                }
                //TODO  新增返回值  返回 主/子 流程 审批记录

                List<ProcessRecordListVo> recordListVos = getProcessRecordListVos(processId, 0);
                vo.setTaskRecords(recordListVos);
                vo.setOtherProcessApproveRecord(otherProcessApproveRecord);
            }
            //如果找不到 就代表  开始节点之后 就是外部流程 或者 子流程  所以 返回开始节点数据就行
            else {
                Optional<HistoricVariableInstance> relationTasksOp = variableInstanceList.stream().filter(x -> x.getName().equals(WorkflowConstant.RELATION_TASK_KEY)).findFirst();

                //如果关联任务不为空
                if (relationTasksOp.isPresent()) {
                    List<LaunchRelationTaskDto> relationTasks = Convert.toList(LaunchRelationTaskDto.class, relationTasksOp.get().getValue());

                    if (relationTasks != null && relationTasks.size() > 0) {

                        List<Long> relationSchemaIds = relationTasks.stream().map(LaunchRelationTaskDto::getSchemaId).collect(Collectors.toList());

                        LambdaQueryWrapper<WorkflowSchema> queryWrapper = new LambdaQueryWrapper<>();
                        queryWrapper.in(WorkflowSchema::getId, relationSchemaIds);
                        queryWrapper.select(WorkflowSchema::getId, WorkflowSchema::getName);
                        List<WorkflowSchema> workflowSchemas = workflowSchemaMapper.selectList(queryWrapper);
                        List<UserTaskRelationTaskVo> relationTasksVo = new ArrayList<>(workflowSchemas.size());

                        for (LaunchRelationTaskDto relationTask : relationTasks) {
                            UserTaskRelationTaskVo relationTaskVo = new UserTaskRelationTaskVo();
                            relationTaskVo.setTaskId(relationTask.getTaskId());
                            relationTaskVo.setSchemaId(relationTask.getSchemaId());
                            Optional<WorkflowSchema> thisSchema = workflowSchemas.stream().filter(schema -> schema.getId().equals(relationTask.getSchemaId())).findFirst();
                            thisSchema.ifPresent(schema -> {
                                relationTaskVo.setSchemaName(schema.getName());
                            });
                            relationTasksVo.add(relationTaskVo);
                        }
                        vo.setRelationTasks(relationTasksVo);
                    }

                }

                //如果没有用户任务  开始节点可能会有表单  查看必须显示开始节点表单数据
                Optional<Map<String, Object>> startNodeConfig = workflowSchemaConfig.getChildNodeConfig().stream().filter(c -> c.containsValue(WorkflowConstant.START_NODE_DEFAULT_ID)).findFirst();

                if (!startNodeConfig.isPresent()) {
                    throw new MyException("找不到当前节点的配置信息！");
                }

                StartNodeConfig startEventConfig = Convert.convert(StartNodeConfig.class, startNodeConfig.get());

                List<Long> formIds = startEventConfig.getFormConfigs().stream().map(FormConfig::getFormId).collect(Collectors.toList());
                String[] formKeys = startEventConfig.getFormConfigs().stream().map(FormConfig::getKey).toArray(String[]::new);
                List<FormTemplate> formTemplates = formTemplateMapper.selectList(Wrappers.lambdaQuery(FormTemplate.class).in(formIds.size() > 0, FormTemplate::getId, formIds));

                List<HistoricVariableInstance> allFormData = historyService.createHistoricVariableInstanceQuery()
                        .processInstanceId(processId)
                        .variableNameIn(formKeys)
                        .list();


                List<UserTaskFormInfoVo> formInfoVos = new ArrayList<>(startEventConfig.getFormConfigs().size());
                for (FormConfig formConfig : startEventConfig.getFormConfigs()) {

                    UserTaskFormInfoVo formInfoVo = new UserTaskFormInfoVo();
                    formInfoVo.setFormType(formConfig.getFormType());
                    formInfoVo.setFormConfig(formConfig);

                    if (allFormData.stream().anyMatch(x -> x.getName().equals(formConfig.getKey()))) {
                        HistoricVariableInstance historicVariableInstance = allFormData.stream().filter(x -> x.getName().equals(formConfig.getKey())).findFirst().orElse(new HistoricVariableInstanceEntity());
                        formInfoVo.setFormData(Convert.toMap(String.class, Object.class, historicVariableInstance.getValue()));
                    }

                    //TODO 系统表单 和 自定义表单 走同一套流程  都是使用json  以后看能否有更好的解决方案 解决用户手动修改系统表单代码
                    if (formConfig.getFormType() == FormTemplateType.CUSTOM.getCode() || formConfig.getFormType() == FormTemplateType.SYSTEM.getCode()) {
                        Optional<FormTemplate> templateOp = formTemplates.stream().filter(f -> Objects.equals(f.getId(), formConfig.getFormId())).findFirst();
                        templateOp.ifPresent(t -> formInfoVo.setFormJson(t.getFormJson()));
                    }

                    formInfoVos.add(formInfoVo);
                }
                vo.setFormInfos(formInfoVos);
                vo.setSchemaInfo(BeanUtil.toBean(workflowSchema, WorkflowSchemaInfoVo.class));

                List<Map<String, Object>> otherProcessApproveRecord = new ArrayList<>();

                //此流程 是否有主流程  如果有 代表他是某个流程的子流程
                if (StrUtil.isNotBlank(superProcessInstanceId)) {

                    HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(superProcessInstanceId).variableName(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY).singleResult();
                    //获取主流程审批记录
                    List<ProcessRecordListVo> processRecordListVos = getProcessRecordListVos(superProcessInstanceId, 0);

                    Map<String, Object> superProcessMap = new HashMap<>();
                    superProcessMap.put("schemaName", historicVariableInstance.getValue() + "(上级流程)");
                    superProcessMap.put("records", processRecordListVos);

                    otherProcessApproveRecord.add(superProcessMap);

                }


                if (childProcess.size() > 0) {
                    String[] strings = childProcess.stream().map(HistoricProcessInstance::getId).toArray(String[]::new);

                    List<HistoricVariableInstance> childSchemaNameVarList = historyService.createHistoricVariableInstanceQuery().processInstanceIdIn(strings).variableName(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY).list();

                    for (HistoricProcessInstance process : childProcess) {
                        Optional<HistoricVariableInstance> varOp = childSchemaNameVarList.stream().filter(x -> x.getProcessInstanceId().equals(process.getId())).findFirst();

                        varOp.ifPresent(schemaNameVar -> {
                            Map<String, Object> superProcessMap = new HashMap<>();
                            superProcessMap.put("schemaName", schemaNameVar.getValue() + "(下级流程)");

                            //获取主流程审批记录
                            List<ProcessRecordListVo> processRecordListVos = getProcessRecordListVos(schemaNameVar.getProcessInstanceId(), 0);
                            superProcessMap.put("records", processRecordListVos);

                            otherProcessApproveRecord.add(superProcessMap);
                        });
                    }
                }
                //TODO  新增返回值  返回 主/子 流程 审批记录

                List<ProcessRecordListVo> recordListVos = getProcessRecordListVos(processId, 0);
                vo.setTaskRecords(recordListVos);
                vo.setOtherProcessApproveRecord(otherProcessApproveRecord);
            }
        }
        //判断流程是否完全没有用户任务
        else {
            Optional<HistoricVariableInstance> relationTasksOp = variableInstanceList.stream().filter(x -> x.getName().equals(WorkflowConstant.RELATION_TASK_KEY)).findFirst();


            //如果关联任务不为空
            if (relationTasksOp.isPresent()) {
                List<LaunchRelationTaskDto> relationTasks = Convert.toList(LaunchRelationTaskDto.class, relationTasksOp.get().getValue());

                if (relationTasks != null && relationTasks.size() > 0) {

                    List<Long> relationSchemaIds = relationTasks.stream().map(LaunchRelationTaskDto::getSchemaId).collect(Collectors.toList());

                    LambdaQueryWrapper<WorkflowSchema> queryWrapper = new LambdaQueryWrapper<>();
                    queryWrapper.in(WorkflowSchema::getId, relationSchemaIds);
                    queryWrapper.select(WorkflowSchema::getId, WorkflowSchema::getName);
                    List<WorkflowSchema> workflowSchemas = workflowSchemaMapper.selectList(queryWrapper);
                    List<UserTaskRelationTaskVo> relationTasksVo = new ArrayList<>(workflowSchemas.size());

                    for (LaunchRelationTaskDto relationTask : relationTasks) {
                        UserTaskRelationTaskVo relationTaskVo = new UserTaskRelationTaskVo();
                        relationTaskVo.setTaskId(relationTask.getTaskId());
                        relationTaskVo.setSchemaId(relationTask.getSchemaId());
                        Optional<WorkflowSchema> thisSchema = workflowSchemas.stream().filter(schema -> schema.getId().equals(relationTask.getSchemaId())).findFirst();
                        thisSchema.ifPresent(schema -> {
                            relationTaskVo.setSchemaName(schema.getName());
                        });
                        relationTasksVo.add(relationTaskVo);
                    }
                    vo.setRelationTasks(relationTasksVo);
                }

            }

            //如果没有用户任务  开始节点可能会有表单  查看必须显示开始节点表单数据
            Optional<Map<String, Object>> startNodeConfig = workflowSchemaConfig.getChildNodeConfig().stream().filter(c -> c.containsValue(WorkflowConstant.START_NODE_DEFAULT_ID)).findFirst();

            if (!startNodeConfig.isPresent()) {
                throw new MyException("找不到当前节点的配置信息！");
            }

            StartNodeConfig startEventConfig = Convert.convert(StartNodeConfig.class, startNodeConfig.get());

            List<Long> formIds = startEventConfig.getFormConfigs().stream().map(FormConfig::getFormId).collect(Collectors.toList());
            String[] formKeys = startEventConfig.getFormConfigs().stream().map(FormConfig::getKey).toArray(String[]::new);
            List<FormTemplate> formTemplates = formTemplateMapper.selectList(Wrappers.lambdaQuery(FormTemplate.class).in(formIds.size() > 0, FormTemplate::getId, formIds));

            List<HistoricVariableInstance> allFormData = historyService.createHistoricVariableInstanceQuery()
                    .processInstanceId(processId)
                    .variableNameIn(formKeys)
                    .list();


            List<UserTaskFormInfoVo> formInfoVos = new ArrayList<>(startEventConfig.getFormConfigs().size());
            for (FormConfig formConfig : startEventConfig.getFormConfigs()) {

                UserTaskFormInfoVo formInfoVo = new UserTaskFormInfoVo();
                formInfoVo.setFormType(formConfig.getFormType());
                formInfoVo.setFormConfig(formConfig);

                if (allFormData.stream().anyMatch(x -> x.getName().equals(formConfig.getKey()))) {
                    HistoricVariableInstance historicVariableInstance = allFormData.stream().filter(x -> x.getName().equals(formConfig.getKey())).findFirst().orElse(new HistoricVariableInstanceEntity());
                    formInfoVo.setFormData(Convert.toMap(String.class, Object.class, historicVariableInstance.getValue()));
                }

                //TODO 系统表单 和 自定义表单 走同一套流程  都是使用json  以后看能否有更好的解决方案 解决用户手动修改系统表单代码
                if (formConfig.getFormType() == FormTemplateType.CUSTOM.getCode() || formConfig.getFormType() == FormTemplateType.SYSTEM.getCode()) {
                    Optional<FormTemplate> templateOp = formTemplates.stream().filter(f -> Objects.equals(f.getId(), formConfig.getFormId())).findFirst();
                    templateOp.ifPresent(t -> formInfoVo.setFormJson(t.getFormJson()));
                }

                formInfoVos.add(formInfoVo);
            }
            vo.setFormInfos(formInfoVos);
            vo.setSchemaInfo(BeanUtil.toBean(workflowSchema, WorkflowSchemaInfoVo.class));

            List<Map<String, Object>> otherProcessApproveRecord = new ArrayList<>();

            //此流程 是否有主流程  如果有 代表他是某个流程的子流程
            if (StrUtil.isNotBlank(superProcessInstanceId)) {

                HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(superProcessInstanceId).variableName(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY).singleResult();
                //获取主流程审批记录
                List<ProcessRecordListVo> processRecordListVos = getProcessRecordListVos(superProcessInstanceId, 0);

                Map<String, Object> superProcessMap = new HashMap<>();
                superProcessMap.put("schemaName", historicVariableInstance.getValue() + "(上级流程)");
                superProcessMap.put("records", processRecordListVos);

                otherProcessApproveRecord.add(superProcessMap);

            }


            if (childProcess.size() > 0) {
                String[] strings = childProcess.stream().map(HistoricProcessInstance::getId).toArray(String[]::new);

                List<HistoricVariableInstance> childSchemaNameVarList = historyService.createHistoricVariableInstanceQuery().processInstanceIdIn(strings).variableName(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY).list();

                for (HistoricProcessInstance process : childProcess) {
                    Optional<HistoricVariableInstance> varOp = childSchemaNameVarList.stream().filter(x -> x.getProcessInstanceId().equals(process.getId())).findFirst();

                    varOp.ifPresent(schemaNameVar -> {
                        Map<String, Object> superProcessMap = new HashMap<>();
                        superProcessMap.put("schemaName", schemaNameVar.getValue() + "(下级流程)");

                        //获取主流程审批记录
                        List<ProcessRecordListVo> processRecordListVos = getProcessRecordListVos(schemaNameVar.getProcessInstanceId(), 0);
                        superProcessMap.put("records", processRecordListVos);

                        otherProcessApproveRecord.add(superProcessMap);
                    });
                }
            }
            //TODO  新增返回值  返回 主/子 流程 审批记录

            List<ProcessRecordListVo> recordListVos = getProcessRecordListVos(processId, 0);
            vo.setTaskRecords(recordListVos);
            vo.setOtherProcessApproveRecord(otherProcessApproveRecord);

        }


        return vo;

    }

    @Override
    public AllRecordListVo getAllRecordInfoByProcessId(String processId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult();
        //查询当前流程是否包含父亲流程
        String superProcessInstanceId = historicProcessInstance.getSuperProcessInstanceId();
        //查看当前流程 是否包含子流程
        List<HistoricProcessInstance> childProcess = historyService.createHistoricProcessInstanceQuery().superProcessInstanceId(processId).list();

        AllRecordListVo vo = new AllRecordListVo();
        List<Map<String, Object>> otherProcessApproveRecord = new ArrayList<>();
        //此流程 是否有主流程  如果有 代表他是某个流程的子流程
        if (StrUtil.isNotBlank(superProcessInstanceId)) {

            HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(superProcessInstanceId).variableName(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY).singleResult();
            //获取主流程审批记录
            List<ProcessRecordListVo> processRecordListVos = getProcessRecordListVos(superProcessInstanceId, 1);

            Map<String, Object> superProcessMap = new HashMap<>();
            superProcessMap.put("schemaName", historicVariableInstance.getValue() + "(上级流程)");
            superProcessMap.put("records", processRecordListVos);

            otherProcessApproveRecord.add(superProcessMap);

        }

        if (childProcess.size() > 0) {
            String[] strings = childProcess.stream().map(HistoricProcessInstance::getId).toArray(String[]::new);

            List<HistoricVariableInstance> childSchemaNameVarList = historyService.createHistoricVariableInstanceQuery().processInstanceIdIn(strings).variableName(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY).list();

            for (HistoricProcessInstance process : childProcess) {
                Optional<HistoricVariableInstance> varOp = childSchemaNameVarList.stream().filter(x -> x.getProcessInstanceId().equals(process.getId())).findFirst();

                varOp.ifPresent(schemaNameVar -> {
                    Map<String, Object> superProcessMap = new HashMap<>();
                    superProcessMap.put("schemaName", schemaNameVar.getValue() + "(下级流程)");

                    //获取主流程审批记录
                    List<ProcessRecordListVo> processRecordListVos = getProcessRecordListVos(schemaNameVar.getProcessInstanceId(), 1);
                    superProcessMap.put("records", processRecordListVos);

                    otherProcessApproveRecord.add(superProcessMap);
                });
            }
        }
        //TODO  新增返回值  返回 主/子 流程 审批记录

        List<ProcessRecordListVo> recordListVos = getProcessRecordListVos(processId, 1);
        vo.setTaskRecords(recordListVos);
        vo.setOtherProcessApproveRecord(otherProcessApproveRecord);
        return vo;
    }

    @Override
    public RecycleProcessInfoVo getRecycleProcessInfo(String processId) {

        RecycleProcessInfoVo vo = new RecycleProcessInfoVo();

        List<HistoricVariableInstance> list = historyService.createHistoricVariableInstanceQuery().processInstanceId(processId)
                .variableNameLike(WorkflowConstant.PROCESS_FORMDATA_PREFIX_KEY + StringPool.PERCENT).list();
        HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(processId)
                .variableName(WorkflowConstant.PROCESS_SCHEMA_ID_KEY).singleResult();


        if (historicVariableInstance == null) {
            throw new MyException("找不到当前模板id");

        }

        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, historicVariableInstance.getValue()));

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);

        List<RelationProcessConfig> relationProcessConfig = workflowSchemaConfig.getProcessConfig().getRelationProcessConfigs();

        List<Long> relationSchemaIds = relationProcessConfig.stream().map(RelationProcessConfig::getId).collect(Collectors.toList());

        if (relationSchemaIds.size() > 0) {
            LambdaQueryWrapper<WorkflowSchema> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(WorkflowSchema::getId, relationSchemaIds);
            queryWrapper.select(WorkflowSchema::getId, WorkflowSchema::getName);
            List<WorkflowSchema> workflowSchemas = workflowSchemaMapper.selectList(queryWrapper);

            List<StartProcessRelationTaskVo> relationTasks = new ArrayList<>(workflowSchemas.size());
            for (WorkflowSchema schema : workflowSchemas) {
                StartProcessRelationTaskVo relationTaskVo = new StartProcessRelationTaskVo();
                relationTaskVo.setSchemaId(schema.getId());
                relationTaskVo.setSchemaName(schema.getName());
                relationTasks.add(relationTaskVo);
            }
            vo.setRelationTasks(relationTasks);
        }


        Map<String, Map<String, Object>> formDatas = new HashMap<>();
        for (HistoricVariableInstance variableInstance : list) {
            formDatas.put(variableInstance.getName(), Convert.toMap(String.class, Object.class, variableInstance.getValue()));
        }


        vo.setFormDatas(formDatas);
        vo.setSchemaInfo(BeanUtil.toBean(workflowSchema, WorkflowSchemaInfoVo.class));
        return vo;
    }


    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public List<LaunchAndApproveVo> newLaunch(LaunchDto dto) {
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectById(dto.getSchemaId());

        if (workflowSchema == null) {
            throw new MyException("找不到此流程！");
        }

        //如果当前模板被禁用 无法发起流程
        if (workflowSchema.getEnabledMark() == EnabledMark.DISABLED.getCode()) {
            throw new MyException("流程被禁用！");
        }

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);
        //将工作流模板存入缓存
        redisUtil.set(WorkflowConstant.SCHEMA_CACHE_PREFIX + workflowSchema.getId(), workflowSchema);

        AuthConfig authConfig = workflowSchemaConfig.getProcessConfig().getAuthConfig();

        //判断是否有发起流程的权限
        if (!WorkFlowUtil.hasPermissions(authConfig)) {
            throw new MyException("没有此流程权限！");
        }
        //获取到流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(workflowSchema.getDeploymentId()).singleResult();

        //流程参数  发布时  存到变量中
        List<ProcessParamConfig> processParamConfigs = workflowSchemaConfig.getProcessConfig().getProcessParamConfigs();

        long count = historyService.createHistoricProcessInstanceQuery().count();
        Map<String, Object> processParam = WorkFlowUtil.getProcessParam(processParamConfigs, workflowSchema, count + 1, null);

        //表单赋值
//        initFormAssignment(dto.getFormData(), workflowSchema, workflowSchemaConfig, processParam);

        //构建流程默认参数 （全局变量 发起人id、发起人名、模板id 、模板名、页面配置参数值、流水号）等
        VariableMap variableMap = initDefaultParam(workflowSchema, processParam, count);
        FormTemplate formTemplate = formTemplateMapper.selectById(workflowSchema.getFormId());

        //根据规则生成流程名称
        String processName = WorkFlowUtil.generatorProcessName(workflowSchema, workflowSchemaConfig, count + 1, dto);
        //根据规则生成流程名称
        variableMap.putValue(WorkflowConstant.PROCESS_NAME, processName);

        Optional<Map<String, Object>> startNodeMap = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(WorkflowConstant.BPMN_XML_START_EVENT_TYPE_NAME)).findFirst();

        if (!startNodeMap.isPresent()) {
            throw new MyException("当前任务没有开始节点");
        }

        //如果关联任务不为空 并且有数据
        if (dto.getRelationTasks() != null && dto.getRelationTasks().size() > 0) {
            variableMap.putValue(WorkflowConstant.RELATION_TASK_KEY, dto.getRelationTasks());
        }


        //将map 转为 java类
        StartNodeConfig startNodeConfig = Convert.convert(StartNodeConfig.class, startNodeMap.get());

        //定义sessionList  存储所有表提交的session  用于最后提交commit  保证事务
        List<Session> sessionList = new ArrayList<>(dto.getFormData().size());

        //定义map 用于存储 formId以及 主键keyValue {formId:keyValue}
//        Map<String, Long> keyValueMap = new HashMap<>(dto.getFormData().size());

        List<WorkflowFormRelation> relations = new ArrayList<>(dto.getFormData().size());

        //提交表单数据  在发起任务之前  原因
        //  1、需要提交数据之后的主键数据也塞入到map中
        //  2、有可能流程图 开始节点之后就是网关 需要提前配置好网关的流转条件  需要提前配置好变量才能发起流程  不然影响流程走向
        //  3、功能页面发起 数据会自带主键  需要调用更新接口
        for (FormConfig formConfig : startNodeConfig.getFormConfigs()) {
            Map<String, Object> thisFormData = dto.getFormData().get(formConfig.getKey());


            //如果其中某一个事务出错，所有表单提交全部回滚
            //formData 新增之后 会将主键加入到 map  所以不需要管
            try {

                //如果提交过 默认是update
                FormExecuteWorkflowUpdateDto executeWorkflowUpdateDto = new FormExecuteWorkflowUpdateDto();
                executeWorkflowUpdateDto.setFormId(formConfig.getFormId());
                executeWorkflowUpdateDto.setFormData(thisFormData);
                variableMap.putValue(formConfig.getKey(), thisFormData);

                Triple<Session, Boolean, Long> sessionLongLongTriple = formExecuteService.workflowAddOrUpdate(executeWorkflowUpdateDto);

                sessionList.add(sessionLongLongTriple.getLeft());

                WorkflowFormRelation formRelation = new WorkflowFormRelation();
                formRelation.setFormId(formConfig.getFormId());
                formRelation.setFormKey(formConfig.getKey());
                formRelation.setFormKeyValue(sessionLongLongTriple.getRight().toString());

                relations.add(formRelation);
            } catch (Exception e) {
                log.error("提交表单数据失败（newLaunch）:",e);
                for (Session session : sessionList) {
                    session.quietRollback();
                }
                throw new MyException("【表单id： " + formConfig.getFormId() + "】 提交错误， 请联系管理员!");
            }

        }

        //将formid 以及 表单主键 存储到变量中
//        variableMap.putValue(WorkflowConstant.PROCESS_FOMRID_KEYVALUE_KEY, keyValueMap);

        //如果没有报错  所有表单提交事务 一起提交  提交有错误 也统一回滚


        try {
            //表单提交完毕后 发起流程
            ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(), variableMap);

            List<Task> list = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();

//            List<LaunchAndApproveVo> result = isPrevChooseApprove(workflowSchemaConfig, list, variableMap);

            //如果不需要指定审批人 默认走自动同意规则
            invokeAutoAgree(processInstance.getId(), workflowSchema.getId(), workflowSchemaConfig, list);

            //重新获取任务节点，判断是否需要指定下一审批人
            list = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();

            List<LaunchAndApproveVo> result = isPrevChooseApprove(workflowSchemaConfig, list, variableMap);

            updateFileInfo(dto.getFileFolderIds(), processInstance.getId());

            //保存 流程 表单 关联 数据
            for (WorkflowFormRelation relation : relations) {
                relation.setProcessId(processInstance.getId());
            }

            formRelationService.saveBatch(relations);

            for (Session session : sessionList) {
                session.getConnection().setAutoCommit(Boolean.FALSE);
                session.commit();
                session.close();
            }

            return result;
        } catch (Exception e) {
            for (Session session : sessionList) {
                session.quietRollback();
            }
            if (ObjectUtil.isNotEmpty(e.getMessage()) && e.getMessage().contains("sequence flow")) {
                throw new MyException("流转条件错误，请检查流转条件设置!");
            } else {
                throw new MyException("表单提交错误， 请联系系统管理员!");
            }
        }

    }

    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public List<LaunchAndApproveVo> reLaunch(ReLaunchDto dto) {


        WorkflowSchema workflowSchema = workflowSchemaMapper.selectById(dto.getSchemaId());

        if (workflowSchema == null) {
            throw new MyException("找不到此流程！");
        }

        //如果当前模板被禁用 无法发起流程
        if (workflowSchema.getEnabledMark() == EnabledMark.DISABLED.getCode()) {
            throw new MyException("流程被禁用！");
        }


        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);
        //将工作流模板存入缓存
        redisUtil.set(WorkflowConstant.SCHEMA_CACHE_PREFIX + workflowSchema.getId(), workflowSchema);

        AuthConfig authConfig = workflowSchemaConfig.getProcessConfig().getAuthConfig();

        //判断是否有发起流程的权限
        if (!WorkFlowUtil.hasPermissions(authConfig)) {
            throw new MyException("没有此流程权限！");
        }

        //获取到流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(workflowSchema.getDeploymentId()).singleResult();

        //流程参数  发布时  存到变量中
        List<ProcessParamConfig> processParamConfigs = workflowSchemaConfig.getProcessConfig().getProcessParamConfigs();

        long count = historyService.createHistoricProcessInstanceQuery().count();

        Map<String, Object> processParam = WorkFlowUtil.getProcessParam(processParamConfigs, workflowSchema, count + 1, null);

        //表单赋值
//        initFormAssignment(dto.getFormData(), workflowSchema, workflowSchemaConfig, processParam);

        //构建流程默认参数 （全局变量 发起人id、发起人名、模板id 、模板名、页面配置参数值、流水号）等
        VariableMap variableMap = initDefaultParam(workflowSchema, processParam, count);

        //根据规则生成流程名称
        String processName = WorkFlowUtil.generatorProcessName(workflowSchema, workflowSchemaConfig, count + 1, null);
        //根据规则生成流程名称
        variableMap.putValue(WorkflowConstant.PROCESS_NAME, processName);

        Optional<Map<String, Object>> startNodeMap = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(WorkflowConstant.BPMN_XML_START_EVENT_TYPE_NAME)).findFirst();

        if (!startNodeMap.isPresent()) {
            throw new MyException("当前任务没有开始节点");
        }

        //如果关联任务不为空 并且有数据
        if (dto.getRelationTasks() != null && dto.getRelationTasks().size() > 0) {
            variableMap.putValue(WorkflowConstant.RELATION_TASK_KEY, dto.getRelationTasks());
        }

        //将map 转为 java类
        StartNodeConfig startNodeConfig = Convert.convert(StartNodeConfig.class, startNodeMap.get());

        //定义sessionList  存储所有表提交的session  用于最后提交commit  保证事务
        List<Session> sessionList = new ArrayList<>(dto.getFormData().size());

        //定义map 用于存储 formId以及 主键keyValue {formId:keyValue}
//        Map<String, Long> keyValueMap = new HashMap<>(dto.getFormData().size());

        List<WorkflowFormRelation> relations = new ArrayList<>(dto.getFormData().size());

        //提交表单数据  在发起任务之前  原因
        //  1、需要提交数据之后的主键数据也塞入到map中
        //  2、有可能流程图 开始节点之后就是网关 需要提前配置好网关的流转条件  需要提前配置好变量才能发起流程  不然影响流程走向
        //  3、功能页面发起 数据会自带主键  需要调用更新接口
        for (FormConfig formConfig : startNodeConfig.getFormConfigs()) {
            Map<String, Object> thisFormData = dto.getFormData().get(formConfig.getKey());


            //如果其中某一个事务出错，所有表单提交全部回滚
            //formData 新增之后 会将主键加入到 map  所以不需要管
            try {

                //如果提交过 默认是update
                FormExecuteWorkflowUpdateDto executeWorkflowUpdateDto = new FormExecuteWorkflowUpdateDto();
                executeWorkflowUpdateDto.setFormId(formConfig.getFormId());
                executeWorkflowUpdateDto.setFormData(thisFormData);
                variableMap.putValue(formConfig.getKey(), thisFormData);

                Triple<Session, Boolean, Long> sessionLongLongTriple = formExecuteService.workflowAddOrUpdate(executeWorkflowUpdateDto);

                sessionList.add(sessionLongLongTriple.getLeft());

                WorkflowFormRelation formRelation = new WorkflowFormRelation();
                formRelation.setFormId(formConfig.getFormId());
                formRelation.setFormKey(formConfig.getKey());
                formRelation.setFormKeyValue(sessionLongLongTriple.getRight().toString());

                relations.add(formRelation);
            } catch (Exception e) {
                log.error("提交表单数据失败（reLaunch）:",e);
                for (Session session : sessionList) {
                    session.quietRollback();
                }
                throw new MyException("【表单id： " + formConfig.getFormId() + "】 提交错误， 请联系管理员!");
            }

        }

        //将formid 以及 表单主键 存储到变量中
//        variableMap.putValue(WorkflowConstant.PROCESS_FOMRID_KEYVALUE_KEY, keyValueMap);

        //如果没有报错  所有表单提交事务 一起提交  提交有错误 也统一回滚


        try {

            //表单提交完毕后 发起流程
            ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(), variableMap);

            List<Task> list = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();

            List<LaunchAndApproveVo> result = isPrevChooseApprove(workflowSchemaConfig, list, variableMap);

            //如果不需要指定审批人 默认走自动同意规则
            invokeAutoAgree(processInstance.getId(), workflowSchema.getId(), workflowSchemaConfig, list);

            updateFileInfo(dto.getFileFolderIds(), processInstance.getId());

            //保存 流程 表单 关联 数据
            for (WorkflowFormRelation relation : relations) {
                relation.setProcessId(processInstance.getId());
            }

            formRelationService.saveBatch(relations);


            for (Session session : sessionList) {
                session.getConnection().setAutoCommit(Boolean.FALSE);
                session.commit();
                session.close();
            }

            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(dto.getProcessId()).singleResult();
            List<HistoricProcessInstance> childProcessinstance = historyService.createHistoricProcessInstanceQuery().superProcessInstanceId(dto.getProcessId()).list();

            for (HistoricProcessInstance processInstance1 : childProcessinstance) {

                //获取到当前活动的实例
                ActivityInstance activityInstance = runtimeService.getActivityInstance(processInstance1.getId());

                //先停止当前活动示例  然后  关闭流程
                runtimeService.createProcessInstanceModification(processInstance1.getId())
                        .cancelActivityInstance(activityInstance.getId())
                        .cancelAllForActivity(activityInstance.getId())
                        .setAnnotation("【重新发起流程】")
                        .execute();
            }


            //获取到当前活动的实例
            ActivityInstance activityInstance = runtimeService.getActivityInstance(dto.getProcessId());

            //如果获取不到当前活动实例  证明此流程  当前可能是在外部流程中  无需其他操作 关闭子流程 即可
            if (activityInstance != null) {
                runtimeService.deleteProcessInstance(dto.getProcessId(), "【重新发起流程】");
            }

            return result;
        } catch (Exception e) {
            for (Session session : sessionList) {
                session.quietRollback();
            }
            if (e.getMessage().contains("No outgoing sequence flow")) {
                throw new MyException("流转条件错误，请检查流转条件设置!");
            } else {
                throw new MyException("表单提交错误， 请联系系统管理员!");
            }
        }finally {
            for (Session session : sessionList) {
                session.close();
            }
        }

    }


    private void updateFileInfo(List<Long> fileFolderIds, String processId) {
        if (fileFolderIds != null && fileFolderIds.size() > 0) {
            File file = new File();
            file.setProcessId(processId);
            LambdaQueryWrapper<File> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.in(File::getFolderId, fileFolderIds);
            fileService.update(file, lambdaQueryWrapper);
        }
    }

    @Override
    public long pendingCount() {

        SaSession tokenSession = StpUtil.getTokenSession();
        User user = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());

        TaskQuery taskQuery = taskService.createTaskQuery()
                .active()
                .taskVariableValueEquals(WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode())
                .processVariableValueEquals(WorkflowConstant.PROCESS_ISRECYCLE_FLAG_KEY, WorkflowIsRecycleType.NO.getCode())
                .taskVariableValueLike(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StringPool.PERCENT + user.getId() + StringPool.PERCENT);
        return taskQuery.count();
    }

    @Override
    public PageOutput<PendingTaskVo> pending(PendingTaskPageDto dto) {

        SaSession tokenSession = StpUtil.getTokenSession();
        User user = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());

        TaskQuery taskQuery = taskService.createTaskQuery()
                .active()
                .taskVariableValueEquals(WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode())
                .processVariableValueEquals(WorkflowConstant.PROCESS_ISRECYCLE_FLAG_KEY, WorkflowIsRecycleType.NO.getCode())
                .taskVariableValueLike(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StringPool.PERCENT + user.getId() + StringPool.PERCENT);
        //
        if (dto.getStartTime() != null) {
            taskQuery.taskCreatedAfter(WorkFlowUtil.getStartOfDay(dto.getStartTime()));
        }
        if (dto.getEndTime() != null) {
            taskQuery.taskCreatedBefore(WorkFlowUtil.getEndOfDay(dto.getEndTime()));
        }

        if (StrUtil.isNotBlank(dto.getKeyword())) {
            taskQuery.or()
                    .processVariableValueLike(WorkflowConstant.PROCESS_NAME, StringPool.PERCENT + dto.getKeyword() + StringPool.PERCENT)
                    .endOr();
        } else {
            if (StrUtil.isNotBlank(dto.getSerialNumber()) && StrUtil.isNumeric(dto.getSerialNumber())) {//数字类型，直接查询
                taskQuery.processVariableValueEquals(WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY, Long.valueOf(dto.getSerialNumber()));
            }
            if (StrUtil.isNotBlank(dto.getName())) {
                taskQuery.processVariableValueLike(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY, StringPool.PERCENT + dto.getName() + StringPool.PERCENT);
            }
            if (StrUtil.isNotBlank(dto.getOriginator())) {
                taskQuery.or()
                        .processVariableValueEquals(WorkflowConstant.PROCESS_START_USER_NAME_KEY, dto.getOriginator())
                        .processVariableValueEquals(WorkflowConstant.PROCESS_START_USER_ID_KEY, dto.getOriginator())
                        .endOr();
            }
            if (StrUtil.isNotBlank(dto.getTaskName())) {
                taskQuery.taskNameLike(StringPool.PERCENT + dto.getTaskName() + StringPool.PERCENT);
            }
        }


        long count = taskQuery.count();

        List<Task> tasks = taskQuery.orderByTaskCreateTime().desc().listPage(Convert.toInt((dto.getLimit() - 1) * dto.getSize()), dto.getSize());


        List<PendingTaskVo> result = new ArrayList<>(tasks.size());

        //获取到所有taskid  去extra表一次性查出数据
        List<String> taskIds = tasks.stream().map(Task::getId).collect(Collectors.toList());

        if (taskIds.size() > 0) {

            List<WorkflowExtra> workflowExtras = workflowExtraMapper.selectList(Wrappers.lambdaQuery(WorkflowExtra.class).in(WorkflowExtra::getTaskId, taskIds));
            for (Task task : tasks) {
                PendingTaskVo vo = new PendingTaskVo();
                Optional<WorkflowExtra> extra = workflowExtras.stream().filter(x -> x.getTaskId().equals(task.getId())).findFirst();
                extra.ifPresent(x -> {
                    vo.setId(x.getId());
                    vo.setSerialNumber(x.getSerialNumber());
                    vo.setSchemaName(x.getSchemaName());
                    vo.setStartUserName(x.getStartUserName());
                    vo.setSchemaId(x.getSchemaId());
                    vo.setSerialNumber(x.getSerialNumber());
                    vo.setCurrentProgress(x.getCurrentProgress());
                    vo.setProcessName(x.getProcessName());
                });
                vo.setProcessId(task.getProcessInstanceId());
                vo.setTaskId(task.getId());
                vo.setTaskName(task.getName());
                vo.setStartTime(DateUtil.toLocalDateTime(task.getCreateTime()));
                result.add(vo);
            }
        }
        if (StrUtil.isNotBlank(dto.getSerialNumber()) && !StrUtil.isNumeric(dto.getSerialNumber())) {//流水号不为数字类型，直接对结果集置空
            result.clear();
        }
        PageOutput<PendingTaskVo> output = new PageOutput<>();
        output.setCurrentPage(dto.getLimit());
        output.setPageSize(dto.getSize());
        output.setTotal(Convert.toInt(count));
        output.setList(result);
        return output;
    }

    @Override
    public Page<PendingTaskVo> myExamine(Page<PendingTaskVo> page, MyExaminePageDto dto) {
        //获取登录者信息
        SaSession tokenSession = StpUtil.getTokenSession();
        User user = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
        List<String> taskIds = new ArrayList<>();
        List<WorkflowApproveRecord> list = approveRecordService.list(
            new QueryWrapper<WorkflowApproveRecord>().lambda()
            .eq(WorkflowApproveRecord::getApproveUserId, user.getId())
        );
        taskIds = list.stream().map(WorkflowApproveRecord::getTaskId).collect(Collectors.toList());
//        if(dto.getStatus() != null && dto.getStatus() == 1){
//
//        }else if(dto.getStatus() != null && dto.getStatus() == 0){
//            TaskQuery taskQuery = taskService.createTaskQuery()
//                    .active()
//                    .taskVariableValueEquals(WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode())
//                    .processVariableValueEquals(WorkflowConstant.PROCESS_ISRECYCLE_FLAG_KEY, WorkflowIsRecycleType.NO.getCode())
//                    .taskVariableValueLike(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StringPool.PERCENT + user.getId() + StringPool.PERCENT);
//            //
//            if (dto.getStartTime() != null) {
//                taskQuery.taskCreatedAfter(WorkFlowUtil.getStartOfDay(dto.getStartTime()));
//            }
//            if (dto.getEndTime() != null) {
//                taskQuery.taskCreatedBefore(WorkFlowUtil.getEndOfDay(dto.getEndTime()));
//            }
//
//            if (StrUtil.isNotBlank(dto.getKeyword())) {
//                taskQuery.or()
//                        .processVariableValueLike(WorkflowConstant.PROCESS_NAME, StringPool.PERCENT + dto.getKeyword() + StringPool.PERCENT)
//                        .endOr();
//            } else {
//                if (StrUtil.isNotBlank(dto.getSerialNumber()) && StrUtil.isNumeric(dto.getSerialNumber())) {//数字类型，直接查询
//                    taskQuery.processVariableValueEquals(WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY, Long.valueOf(dto.getSerialNumber()));
//                }
//                if (StrUtil.isNotBlank(dto.getName())) {
//                    taskQuery.processVariableValueLike(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY, StringPool.PERCENT + dto.getName() + StringPool.PERCENT);
//                }
//                if (StrUtil.isNotBlank(dto.getOriginator())) {
//                    taskQuery.or()
//                            .processVariableValueEquals(WorkflowConstant.PROCESS_START_USER_NAME_KEY, dto.getOriginator())
//                            .processVariableValueEquals(WorkflowConstant.PROCESS_START_USER_ID_KEY, dto.getOriginator())
//                            .endOr();
//                }
//                if (StrUtil.isNotBlank(dto.getTaskName())) {
//                    taskQuery.taskNameLike(StringPool.PERCENT + dto.getTaskName() + StringPool.PERCENT);
//                }
//            }
//
//            List<Task> tasks = taskQuery.orderByTaskCreateTime().desc().listPage(Convert.toInt((dto.getLimit() - 1) * dto.getSize()), dto.getSize());
//            taskIds = tasks.stream().map(Task::getId).collect(Collectors.toList());
//        }
        dto.setTaskIds(taskIds);
        return xjrWorkflowExtraMapper.myApproveRecord(page, dto);
    }


    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public List<LaunchAndApproveVo> newApprove(ApproveDto dto) {
        //根据taskid  获取任务信息
        Task task = taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();

        if (task == null) {
            throw new MyException("找不到此任务！");
        }

        //获取当前任务变量中的  schemaId
        Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));

        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, schemaId).select(x -> !x.getProperty().startsWith(DB_FIELD_XML_PREFIX)));

//        //获取工作流配置信息
//        WorkflowSchema workflowSchema = workflowSchemaMapper.selectById(schemaId);
        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);
        //找到当前用户任务节点的配置信息
        Optional<Map<String, Object>> userTaskMapOptional = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(task.getTaskDefinitionKey())).findFirst();
        //判断是否找得到此任务节点 没有则报错
        if (!userTaskMapOptional.isPresent()) {
            throw new MyException("【任务id ：" + task.getId() + "】 用户任务节点配置有误，请联系管理员！");
        }

        //将map 转为 java类
        UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskMapOptional.get());

        //判断 如果有开启签章  就判断签章 和 密码是否正确
        if (userTaskConfig.getOpinionConfig().getEnabled() && userTaskConfig.getOpinionConfig().getSignature() == YesOrNoEnum.YES.getCode() && ObjectUtil.isNotNull(dto.getStampId())) {
            Stamp stamp = stampService.getById(dto.getStampId());
            if (ObjectUtil.isNull(stamp)) {
                throw new MyException("找不到此签章！");
            }
            if (!StrUtil.equals(stamp.getPassword(), dto.getStampPassword())) {
                throw new MyException("签章密码错误！");
            }
        }

        //查出所有表单数据
        VariableMap variableMap = Variables.createVariables();

        //查出所有表单数据
        List<VariableInstance> allFormVar = runtimeService.createVariableInstanceQuery()
                .processInstanceIdIn(task.getProcessInstanceId())
                .variableNameLike(WorkflowConstant.PROCESS_FORMDATA_PREFIX_KEY + StringPool.PERCENT)
                .list();

        for (VariableInstance variableInstance : allFormVar) {
            variableMap.putValue(variableInstance.getName(), variableInstance.getValue());
        }

        //定义sessionList  存储所有表提交的session  用于最后提交commit  保证事务
        List<Session> sessionList = new ArrayList<>(userTaskConfig.getFormConfigs().size());

        //获取流程变量的值
        Object variable = runtimeService.getVariable(task.getProcessInstanceId(), WorkflowConstant.PROCESS_PARAM_KEY);

        Map<String, Object> processParam = Convert.toMap(String.class, Object.class, variable);
//        WorkFlowUtil.invokeFormAssignment(dto.getFormData(), userTaskConfig.getAssignmentConfig(), userTaskConfig.getFormConfigs(), processParam);

        //获取到整个流程已经新增过的表单数据
//        Object keyValueMapObject = runtimeService.getVariable(task.getProcessInstanceId(), WorkflowConstant.PROCESS_FOMRID_KEYVALUE_KEY);
//        Map<String, Long> keyValueMap = Convert.toMap(String.class, Long.class, keyValueMapObject);
//        if (keyValueMap == null) {
//            keyValueMap = new HashMap<>();
//        }

        ButtonConfig buttonConfig = userTaskConfig.getButtonConfigs().stream().filter(b -> b.getButtonCode().equals(dto.getApprovedResult())).findFirst().orElse(new ButtonConfig());

        //如果是其他按钮
        if (dto.getApprovedType() == WorkflowApproveType.OTHER.getCode()) {

            //如果是执行api
            if (buttonConfig.getButtonType() == 2) {
                ApiConfig apiConfig = buttonConfig.getApiConfig();
                IMagicApiService magicApiService = SpringUtil.getBean(IMagicApiService.class);
                MagicAPIService magicAPIService = SpringUtil.getBean(MagicAPIService.class);

                Object varObj = runtimeService.getVariables(task.getProcessInstanceId());
                Map<String, Object> varMap = Convert.toMap(String.class, Object.class, varObj);

                MagicApiInfoVo info = magicApiService.info(apiConfig.getId());

                Map<String, Object> params = new HashMap<>();

                for (ApiRequestParamsConfig requestParamsConfig : apiConfig.getRequestParamsConfigs()) {
                    WorkFlowUtil.initApiParams(processParam, varMap, params, requestParamsConfig);
                }
                for (ApiRequestParamsConfig requestHeaderConfig : apiConfig.getRequestHeaderConfigs()) {
                    WorkFlowUtil.initApiParams(processParam, varMap, params, requestHeaderConfig);
                }
                for (ApiRequestParamsConfig requestBodyConfig : apiConfig.getRequestBodyConfigs()) {
                    WorkFlowUtil.initApiParams(processParam, varMap, params, requestBodyConfig);
                }
                magicAPIService.execute(info.getMethod(), info.getPath(), params);
            }
            //如果是执行脚本
            else {
                Map<String, Object> param = new HashMap<>();
                param.put("language", buttonConfig.getScriptLanguage());
                param.put("content", buttonConfig.getScriptContent());
                redisUtil.set(WorkflowConstant.BUTTON_EVENT_CACHE_PRE + task.getId(), param, 10);
            }
        }

        LambdaQueryWrapper<WorkflowFormRelation> eq = Wrappers.lambdaQuery(WorkflowFormRelation.class).eq(WorkflowFormRelation::getProcessId, task.getProcessInstanceId());
        List<WorkflowFormRelation> formRelationList = formRelationService.list(eq);
        List<WorkflowFormRelation> newFormRelationList = new ArrayList<>();

        List<LaunchAndApproveVo> result = new ArrayList<>();

        try {
            for (FormConfig formConfig : userTaskConfig.getFormConfigs()) {

                Map<String, Object> formData = dto.getFormData().get(formConfig.getKey());
                variableMap.putValue(formConfig.getKey(), formData);

                //判断此表单是否已经提交过

                if (formRelationList.stream().anyMatch(r -> Objects.equals(r.getFormId(), formConfig.getFormId()) && r.getFormKey().equals(formConfig.getKey()))) {

                    //如果提交过 默认是update
                    FormExecuteWorkflowUpdateDto executeWorkflowUpdateDto = new FormExecuteWorkflowUpdateDto();
                    executeWorkflowUpdateDto.setFormId(formConfig.getFormId());
                    executeWorkflowUpdateDto.setFormData(formData);

                    Triple<Session, Long, Long> sessionLongLongTriple = formExecuteService.workflowUpdate(executeWorkflowUpdateDto);
                    //如果是已经提交过的表单 变量keyValueMap 已经存在 没有必要在新增
                    sessionList.add(sessionLongLongTriple.getLeft());

                } else {
                    //如果没有提交过 默认是add
                    FormExecuteWorkflowAddDto executeWorkflowAddDto = new FormExecuteWorkflowAddDto();
                    executeWorkflowAddDto.setFormId(formConfig.getFormId());
                    executeWorkflowAddDto.setFormData(formData);

                    Triple<Session, Long, Long> sessionLongLongTriple = formExecuteService.workflowAdd(executeWorkflowAddDto);
                    sessionList.add(sessionLongLongTriple.getLeft());

                    WorkflowFormRelation relation = new WorkflowFormRelation();
                    relation.setProcessId(task.getProcessInstanceId());
                    relation.setFormId(formConfig.getFormId());
                    relation.setFormKey(formConfig.getKey());
                    relation.setFormKeyValue(sessionLongLongTriple.getRight().toString());

                    newFormRelationList.add(relation);

//                    keyValueMap.put(formConfig.getKey(), sessionLongLongTriple.getRight());

                }
            }

            formRelationService.saveBatch(newFormRelationList);

            //将formid 以及 表单主键 存储到变量中
//            variableMap.putValue(WorkflowConstant.PROCESS_FOMRID_KEYVALUE_KEY, keyValueMap);

//            //----------------------------------参数赋值------------------------------------------------
//            WorkFlowUtil.invokeParamAssignment(userTaskConfig.getAssignmentConfig(), processParam,workflowSchema);
//
//            variableMap.putValue(WorkflowConstant.PROCESS_PARAM_KEY, processParam);
//
//            //----------------------------------参数赋值------------------------------------------------

            WorkFlowUtil.cacheTaskAssigneeRecord(task.getProcessInstanceId(), task.getId(), StpUtil.getLoginIdAsLong());

            WorkFlowUtil.cacheTaskApproveType(task.getProcessInstanceId(), task.getId(), dto.getApprovedType());

            User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
            //将表单数据 | 流程线数据 | keyValueMap 存入变量
            runtimeService.setVariables(task.getProcessInstanceId(), variableMap);


            List<String> variableNames = ListUtil.toList(
                    WorkflowConstant.PROCESS_SCHEMA_ID_KEY,
                    WorkflowConstant.PROCESS_START_USER_ID_KEY,
                    WorkflowConstant.PROCESS_START_USER_NAME_KEY,
                    WorkflowConstant.PROCESS_SCHEMA_NAME_KEY,
                    WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY);

            Map<String, Object> variableMaps = runtimeService.getVariables(task.getProcessInstanceId(), variableNames);
            //如果有设置 传阅人  加入到变量中
            String circulateMessage = "";
            if (dto.getCirculateConfigs().size() > 0) {
                circulateMessage = initCirculate(task, dto.getCirculateConfigs(), workflowSchemaConfig, userTaskConfig, variableMaps);
            }


            //任务设置审批意见
            taskService.createComment(task.getId(), task.getProcessInstanceId(), dto.getApprovedContent());


            //新增任务审批记录
            WorkflowApproveRecord approveRecord = new WorkflowApproveRecord();
            approveRecord.setSchemaId(schemaId);
            approveRecord.setCurrentProgress(userTaskConfig.getCurrentProgress());
            approveRecord.setSerialNumber(MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY));
            approveRecord.setApproveComment(dto.getApprovedContent());
            approveRecord.setApproveTime(LocalDateTime.now());
            approveRecord.setApproveUserId(StpUtil.getLoginIdAsLong());
            approveRecord.setApproveStamp(dto.getStampId());
            approveRecord.setProcessId(task.getProcessInstanceId());
            approveRecord.setTaskId(task.getId());
            approveRecord.setTaskDefinitionKey(task.getTaskDefinitionKey());
            approveRecord.setTaskName(task.getName());
            approveRecord.setStartUserId(MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_START_USER_ID_KEY));
            approveRecord.setApproveUserPostId(user.getPostId());
            approveRecord.setApproveType(dto.getApprovedType());
            approveRecord.setApproveResult(buttonConfig.getButtonName());

            approveRecordService.save(approveRecord);

            WorkflowExtra extra = new WorkflowExtra();
            extra.setEndTime(LocalDateTime.now());
            extraService.update(extra, Wrappers.lambdaQuery(WorkflowExtra.class).eq(WorkflowExtra::getTaskId, dto.getTaskId()));

            String resultVarName = task.getTaskDefinitionKey() +
                    StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE +
                    WorkflowConstant.BUTTON_APPROVE_VAR_FLAG +
                    StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE +
                    WorkflowConstant.BUTTON_APPROVE_RESULT_VAR_FLAG;

            //审批所选按钮需要保存到变量表中  因为流程线可能用上
            // 先判断是否为会签节点  如果是会签节点 变量数据 会有审批结果
            //  审批结果
            if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() == WorkflowMultiInstanceType.NONE.getCode()) {
                String buttonVarName = task.getTaskDefinitionKey() + StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE + WorkflowConstant.BUTTON_APPROVE_VAR_FLAG;
                runtimeService.setVariable(task.getProcessInstanceId(), buttonVarName, dto.getApprovedResult());
            } else {
                String agreeButtonVarName = task.getTaskDefinitionKey() +
                        StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE +
                        WorkflowConstant.BUTTON_APPROVE_VAR_FLAG +
                        StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE +
                        WorkflowConstant.BUTTON_AGREE_APPROVE_VAR_FLAG;

                String rejectButtonVarName = task.getTaskDefinitionKey() +
                        StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE +
                        WorkflowConstant.BUTTON_APPROVE_VAR_FLAG +
                        StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE +
                        WorkflowConstant.BUTTON_REJECT_APPROVE_VAR_FLAG;

                //如果是点击的同意  默认查询同意 总和 如果有 就 +1  如果没有 设置为1
                if (dto.getApprovedType() == WorkflowApproveType.AGREE.getCode()) {
                    Object agreeObj = runtimeService.getVariable(task.getProcessInstanceId(), agreeButtonVarName);
                    int nowAgreeCount = Convert.toInt(agreeObj) + 1;
                    runtimeService.setVariable(task.getProcessInstanceId(), agreeButtonVarName, nowAgreeCount);


                    //如果是全部 需要全部同意
                    if (userTaskConfig.getCountersignConfig().getFinishType() == WorkflowMultiInstanceFinishType.ALL.getCode()) {
                        Object nrOfInstances = runtimeService.getVariable(task.getExecutionId(), WorkflowConstant.MULTI_INSTANCE_VAR_KEY);
                        if (Objects.equals(nowAgreeCount, Convert.toInt(nrOfInstances))) {
                            runtimeService.setVariable(task.getProcessInstanceId(), resultVarName, Boolean.TRUE);
                        }
                    }

                    //如果是单个 代码只要能执行到此处 就代表已经会签通过了
                    if (userTaskConfig.getCountersignConfig().getFinishType() == WorkflowMultiInstanceFinishType.SINGLE.getCode()) {
                        runtimeService.setVariable(task.getProcessInstanceId(), resultVarName, Boolean.TRUE);
                    }

                    //如果是百分比
                    if (userTaskConfig.getCountersignConfig().getFinishType() == WorkflowMultiInstanceFinishType.PERCENTAGE.getCode()) {
                        Object nrOfInstances = runtimeService.getVariable(task.getExecutionId(), WorkflowConstant.MULTI_INSTANCE_VAR_KEY);

                        //判断是否达到百分比
                        if ((Convert.toDouble(nowAgreeCount) / Convert.toDouble(nrOfInstances) * 100) > Convert.toDouble(userTaskConfig.getCountersignConfig().getPercentage())) {
                            runtimeService.setVariable(task.getProcessInstanceId(), resultVarName, Boolean.TRUE);
                        }

                    }

                } else {
                    Object rejectObj = runtimeService.getVariable(task.getProcessInstanceId(), rejectButtonVarName);
                    int nowRejectCount = Convert.toInt(rejectObj) + 1;
                    runtimeService.setVariable(task.getProcessInstanceId(), rejectButtonVarName, nowRejectCount);

                    //如果是全部 需要全部同意 只要代码进入这里 就代表  已经 会签不通过
                    if (userTaskConfig.getCountersignConfig().getFinishType() == WorkflowMultiInstanceFinishType.ALL.getCode()) {
                        runtimeService.setVariable(task.getProcessInstanceId(), resultVarName, Boolean.FALSE);
                    }
                }

            }

            Object resultName = new Object();
            //获取会签流程的结果，防止会签节点后面直接跟结束节点，导致拿不到流程数据
            if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() != WorkflowMultiInstanceType.NONE.getCode()) {
                resultName = runtimeService.getVariable(task.getProcessInstanceId(), resultVarName);
            }

            //如果是同意  || 拒绝  || 其他  默认都是流程往下走
            if (dto.getApprovedType() == WorkflowApproveType.AGREE.getCode() || dto.getApprovedType() == WorkflowApproveType.DISAGREE.getCode() || dto.getApprovedType() == WorkflowApproveType.OTHER.getCode()) {

                //新增流程发起流程记录
                WorkflowRecord record = new WorkflowRecord();
                record.setNodeId(task.getId());
                record.setNodeName(task.getName());
                record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
                record.setProcessId(task.getProcessInstanceId());
                record.setSchemaId(schemaId);
                record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
                //时间设值必须写在complete上面，防止用户任务节点监听时的流程信息，在次任务前面。
                record.setRecordTime(LocalDateTime.now());
                //当前任务的名称
                String oldTaskName = task.getName();
                record.setCirculateMessage(circulateMessage);

                taskService.complete(task.getId());
                List<Task> list = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
                result = isPrevChooseApprove(workflowSchemaConfig, list, variableMap);

                //下一个节点的任务名称集合
                List<String> nextTaskNameList = list.stream().map(Task::getName).collect(Collectors.toList());

                //如果不需要指定审批人 默认走自动同意规则
                invokeAutoAgree(task.getProcessInstanceId(), workflowSchema.getId(), workflowSchemaConfig, list);

                //新增工作流程信息数据
                addWorkflowRecord(userTaskConfig, nextTaskNameList, user, record, oldTaskName, buttonConfig.getButtonName(), dto.getApprovedContent(), task, resultName);

            }

            //如果是驳回
            if (dto.getApprovedType() == WorkflowApproveType.REJECT.getCode()) {
                //获取到当前活动的实例
                ActivityInstance activityInstance = runtimeService.getActivityInstance(task.getProcessInstanceId());

                //先停止当前活动示例  然后  测回流程到指定节点
                runtimeService.createProcessInstanceModification(task.getProcessInstanceId())
                        .cancelActivityInstance(activityInstance.getId())
                        .setAnnotation("【审批人:" + user.getName() + "】 驳回 【任务:" + task.getId() + "】")
                        .startBeforeActivity(dto.getRejectNodeActivityId())
                        .execute();


                Optional<Map<String, Object>> rejectNodeMap = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(dto.getRejectNodeActivityId())).findFirst();

                rejectNodeMap.ifPresent(node -> {
                    //将map 转为 java类
                    UserTaskConfig rejectUserTaskConfig = Convert.convert(UserTaskConfig.class, node);

                    //新增流程发起流程记录
                    WorkflowRecord record = new WorkflowRecord();
                    record.setNodeId(task.getId());
                    record.setNodeName(task.getName());
                    record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
                    record.setProcessId(task.getProcessInstanceId());
                    record.setSchemaId(schemaId);
                    record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
                    record.setRecordTime(LocalDateTime.now().minusSeconds(+1));//时间设置提前1秒钟，好排序

//                    record.setMessage("【审批人:" + user.getName() + "】 将 【任务:" + task.getName() + "】 驳回到 【任务:" + rejectUserTaskConfig.getName() + "】");
                    record.setMessage("审批信息：【" + user.getName() + "】【驳回】审批,由【" + task.getName() + "】 流转到【" + rejectUserTaskConfig.getName() + "】");
                    workflowRecordMapper.insert(record);
                });

            }
            //如果是结束
            if (dto.getApprovedType() == WorkflowApproveType.FINISH.getCode()) {
                //获取到当前活动的实例
                ActivityInstance activityInstance = runtimeService.getActivityInstance(task.getProcessInstanceId());

                //先停止当前活动示例  然后  关闭流程
                runtimeService.createProcessInstanceModification(task.getProcessInstanceId())
                        .cancelActivityInstance(activityInstance.getId())
                        .cancelAllForActivity(activityInstance.getId())
                        .setAnnotation("【审批人:" + user.getName() + "】 结束 【任务:" + task.getId() + "】")
                        .execute();

                //新增流程发起流程记录
                WorkflowRecord record = new WorkflowRecord();
                record.setNodeId(task.getId());
                record.setNodeName(task.getName());
                record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
                record.setProcessId(task.getProcessInstanceId());
                record.setSchemaId(schemaId);
                record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
                record.setRecordTime(LocalDateTime.now());

                record.setMessage("【审批人:" + user.getName() + "】 结束 【任务:" + task.getName() + "】");

                workflowRecordMapper.insert(record);

            }

            updateFileInfo(dto.getFileFolderIds(), task.getProcessInstanceId());

            for (Session session : sessionList) {
                session.getConnection().setAutoCommit(Boolean.FALSE);
                session.commit();
                session.close();
            }

        } catch (Exception e) {
            for (Session session : sessionList) {
                session.quietRollback();
            }
            if (e.getMessage().contains("sequence flow")) {
                throw new MyException("流转条件错误，请检查流转条件设置!");
            } else {
                throw new MyException("表单提交错误， 请联系系统管理员!");
            }
        }


        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<ApproveMultiVo> approveMulti(ApproveMultiDto dto) {

        List<Task> taskList = taskService.createTaskQuery().taskIdIn(dto.getTaskIds().toArray(new String[0])).list();


        List<String> variableNames = ListUtil.toList(
                WorkflowConstant.PROCESS_SCHEMA_ID_KEY,
                WorkflowConstant.PROCESS_START_USER_ID_KEY,
                WorkflowConstant.PROCESS_START_USER_NAME_KEY,
                WorkflowConstant.PROCESS_SCHEMA_NAME_KEY,
                WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY,
                WorkflowConstant.PROCESS_PARAM_KEY);

        List<HistoricVariableInstance> varList = historyService.createHistoricVariableInstanceQuery()
                .processInstanceIdIn(taskList.stream().map(Task::getProcessInstanceId).toArray(String[]::new))
                .variableNameIn(variableNames.toArray(new String[0]))
                .list();

        List<HistoricVariableInstance> formDataList = historyService.createHistoricVariableInstanceQuery()
                .processInstanceIdIn(taskList.stream().map(Task::getProcessInstanceId).toArray(String[]::new))
                .variableNameLike(WorkflowConstant.PROCESS_FORMDATA_PREFIX_KEY + StringPool.PERCENT)
                .list();

        List<Object> allSchemaId = varList.stream().filter(x -> x.getName().equals(WorkflowConstant.PROCESS_SCHEMA_ID_KEY)).map(HistoricVariableInstance::getValue).collect(Collectors.toList());

        //排除xml 查出数据
        List<WorkflowSchema> workflowSchemaList = workflowSchemaMapper.selectList(Wrappers.lambdaQuery(WorkflowSchema.class).in(WorkflowSchema::getId, allSchemaId).select(x -> !x.getProperty().startsWith(DB_FIELD_XML_PREFIX)));
        User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());

        //定义sessionList  存储所有表提交的session  用于最后提交commit  保证事务
        List<Session> sessionList = new ArrayList<>();
        List<ApproveMultiVo> approveMultiVoList = new ArrayList<>();
        try {
            List<WorkflowExtra> workflowExtras = workflowExtraMapper.selectList(Wrappers.lambdaQuery(WorkflowExtra.class).in(WorkflowExtra::getTaskId, dto.getTaskIds()));
            for (String taskId : dto.getTaskIds()) {
                Task task = taskList.stream().filter(t -> t.getId().equals(taskId)).findFirst().orElse(new TaskEntity());
                HistoricVariableInstance schemaIdInstance = varList.stream().filter(v -> v.getName().equals(WorkflowConstant.PROCESS_SCHEMA_ID_KEY) && v.getProcessInstanceId().equals(task.getProcessInstanceId())).findFirst().orElse(new HistoricVariableInstanceEntity());

                //获取当前任务变量中的  schemaId
                Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));

                WorkflowSchema workflowSchema = workflowSchemaList.stream().filter(w -> w.getId().equals(Convert.toLong(schemaIdInstance.getValue()))).findFirst().orElse(new WorkflowSchema());
                //获取到整个流程模板的配置
                WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);
                //找到当前用户任务节点的配置信息
                Optional<Map<String, Object>> userTaskMapOptional = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(task.getTaskDefinitionKey())).findFirst();
                //判断是否找得到此任务节点 没有则报错
                if (!userTaskMapOptional.isPresent()) {
                    throw new MyException("【任务id ：" + task.getId() + "】 用户任务节点配置有误，请联系管理员！");
                }

                //将map 转为 java类
                UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskMapOptional.get());

                //判断 如果有开启签章  就判断签章 和 密码是否正确
                if (userTaskConfig.getOpinionConfig().getEnabled() && userTaskConfig.getOpinionConfig().getSignature() == YesOrNoEnum.YES.getCode() && ObjectUtil.isNotNull(dto.getStampId())) {
                    Stamp stamp = stampService.getById(dto.getStampId());
                    if (ObjectUtil.isNull(stamp)) {
                        throw new MyException("找不到此签章！");
                    }
                    if (!StrUtil.equals(stamp.getPassword(), dto.getStampPassword())) {
                        throw new MyException("签章密码错误！");
                    }
                }

                HistoricVariableInstance processParamInstance = varList.stream().filter(v -> v.getName().equals(WorkflowConstant.PROCESS_PARAM_KEY) && v.getProcessInstanceId().equals(task.getProcessInstanceId())).findFirst().orElse(new HistoricVariableInstanceEntity());
                //获取流程变量的值
                Map<String, Object> processParam = Convert.toMap(String.class, Object.class, processParamInstance.getValue());

                //获取到当前流程的所有表单数据
                List<HistoricVariableInstance> thisTaskFormData = formDataList.stream().filter(x -> x.getProcessInstanceId().equals(task.getProcessInstanceId())).collect(Collectors.toList());

                //拼凑成表单 Map<String, Map<String, Object>> 类型
                Map<String, Map<String, Object>> allFormData = new HashMap<>();
                VariableMap variableMap = Variables.createVariables();

                for (HistoricVariableInstance variableInstance : thisTaskFormData) {
                    allFormData.put(variableInstance.getName(), Convert.toMap(String.class, Object.class, variableInstance.getValue()));
                }

                WorkFlowUtil.invokeFormAssignment(allFormData, userTaskConfig.getAssignmentConfig(), userTaskConfig.getFormConfigs(), processParam);


                for (FormConfig formConfig : userTaskConfig.getFormConfigs()) {

                    Map<String, Object> formData = allFormData.get(formConfig.getKey());

                    variableMap.putValue(formConfig.getKey(), formData);

                    //表单已经提交过 默认是update
                    FormExecuteWorkflowUpdateDto executeWorkflowUpdateDto = new FormExecuteWorkflowUpdateDto();
                    executeWorkflowUpdateDto.setFormId(formConfig.getFormId());
                    executeWorkflowUpdateDto.setFormData(formData);

                    Triple<Session, Boolean, Long> sessionLongLongTriple = formExecuteService.workflowAddOrUpdate(executeWorkflowUpdateDto);
                    //如果是已经提交过的表单 变量keyValueMap 已经存在 没有必要在新增
                    sessionList.add(sessionLongLongTriple.getLeft());
                }

//                //----------------------------------参数赋值------------------------------------------------
//                WorkFlowUtil.invokeParamAssignment(userTaskConfig.getAssignmentConfig(), processParam,workflowSchema);
//
//                variableMap.putValue(WorkflowConstant.PROCESS_PARAM_KEY, processParam);
//
//                //----------------------------------参数赋值------------------------------------------------

                WorkFlowUtil.cacheTaskAssigneeRecord(task.getProcessInstanceId(), task.getId(), StpUtil.getLoginIdAsLong());

                WorkFlowUtil.cacheTaskApproveType(task.getProcessInstanceId(), task.getId(), dto.getApprovedType());

                //将表单数据 | 流程线数据 | keyValueMap 存入变量
                runtimeService.setVariables(task.getProcessInstanceId(), variableMap);

                //任务设置审批意见
                taskService.createComment(task.getId(), task.getProcessInstanceId(), dto.getApprovedContent());

                HistoricVariableInstance serialNumberInstance = varList.stream().filter(v -> v.getName().equals(WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY) && v.getProcessInstanceId().equals(task.getProcessInstanceId())).findFirst().orElse(new HistoricVariableInstanceEntity());
                HistoricVariableInstance startUserIdInstance = varList.stream().filter(v -> v.getName().equals(WorkflowConstant.PROCESS_START_USER_ID_KEY) && v.getProcessInstanceId().equals(task.getProcessInstanceId())).findFirst().orElse(new HistoricVariableInstanceEntity());

                //新增任务审批记录
                WorkflowApproveRecord approveRecord = new WorkflowApproveRecord();
                approveRecord.setSchemaId(Convert.toLong(schemaIdInstance.getValue()));
                approveRecord.setCurrentProgress(userTaskConfig.getCurrentProgress());
                approveRecord.setSerialNumber(Convert.toLong(serialNumberInstance.getValue()));
                approveRecord.setApproveComment(dto.getApprovedContent());
                approveRecord.setApproveTime(LocalDateTime.now());
                approveRecord.setApproveUserId(StpUtil.getLoginIdAsLong());
                approveRecord.setApproveUserPostId(user.getPostId());
                approveRecord.setApproveStamp(dto.getStampId());
                approveRecord.setProcessId(task.getProcessInstanceId());
                approveRecord.setTaskId(task.getId());
                approveRecord.setTaskDefinitionKey(task.getTaskDefinitionKey());
                approveRecord.setTaskName(task.getName());
                approveRecord.setStartUserId(Convert.toLong(startUserIdInstance.getValue()));

                Optional<ButtonConfig> buttonConfig = userTaskConfig.getButtonConfigs().stream().filter(b -> b.getApproveType() == dto.getApprovedType()).findFirst();
                buttonConfig.ifPresent(button -> {
                    approveRecord.setApproveResult(button.getButtonName());
                });

                approveRecordService.save(approveRecord);

                WorkflowExtra extra = new WorkflowExtra();
                extra.setEndTime(LocalDateTime.now());
                extraService.update(extra, Wrappers.lambdaQuery(WorkflowExtra.class).eq(WorkflowExtra::getTaskId, taskId));

                String resultVarName = task.getTaskDefinitionKey() +
                        StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE +
                        WorkflowConstant.BUTTON_APPROVE_VAR_FLAG +
                        StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE +
                        WorkflowConstant.BUTTON_APPROVE_RESULT_VAR_FLAG;

                //审批所选按钮需要保存到变量表中  因为流程线可能用上
                // 先判断是否为会签节点  如果是会签节点 变量数据 会有审批结果
                //  审批结果
                if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() == WorkflowMultiInstanceType.NONE.getCode()) {
                    String buttonVarName = task.getTaskDefinitionKey() + StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE + WorkflowConstant.BUTTON_APPROVE_VAR_FLAG;
                    if (dto.getApprovedType() == WorkflowApproveType.AGREE.getCode()) {
                        runtimeService.setVariable(task.getProcessInstanceId(), buttonVarName, "agree");
                    }
                    if (dto.getApprovedType() == WorkflowApproveType.DISAGREE.getCode()) {
                        runtimeService.setVariable(task.getProcessInstanceId(), buttonVarName, "disagree");
                    }
                } else {
                    String agreeButtonVarName = task.getTaskDefinitionKey() +
                            StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE +
                            WorkflowConstant.BUTTON_APPROVE_VAR_FLAG +
                            StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE +
                            WorkflowConstant.BUTTON_AGREE_APPROVE_VAR_FLAG;

                    String rejectButtonVarName = task.getTaskDefinitionKey() +
                            StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE +
                            WorkflowConstant.BUTTON_APPROVE_VAR_FLAG +
                            StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE +
                            WorkflowConstant.BUTTON_REJECT_APPROVE_VAR_FLAG;


                    //如果是点击的同意  默认查询同意 总和 如果有 就 +1  如果没有 设置为1
                    if (dto.getApprovedType() == WorkflowApproveType.AGREE.getCode()) {
                        Object agreeObj = runtimeService.getVariable(task.getProcessInstanceId(), agreeButtonVarName);
                        int nowAgreeCount = Convert.toInt(agreeObj) + 1;
                        runtimeService.setVariable(task.getProcessInstanceId(), agreeButtonVarName, nowAgreeCount);


                        //如果是全部 需要全部同意
                        if (userTaskConfig.getCountersignConfig().getFinishType() == WorkflowMultiInstanceFinishType.ALL.getCode()) {
                            Object nrOfInstances = runtimeService.getVariable(task.getExecutionId(), WorkflowConstant.MULTI_INSTANCE_VAR_KEY);
                            if (Objects.equals(nowAgreeCount, Convert.toInt(nrOfInstances))) {
                                runtimeService.setVariable(task.getProcessInstanceId(), resultVarName, Boolean.TRUE);
                            }
                        }

                        //如果是单个 代码只要能执行到此处 就代表已经会签通过了
                        if (userTaskConfig.getCountersignConfig().getFinishType() == WorkflowMultiInstanceFinishType.SINGLE.getCode()) {
                            runtimeService.setVariable(task.getProcessInstanceId(), resultVarName, Boolean.TRUE);
                        }

                        //如果是百分比
                        if (userTaskConfig.getCountersignConfig().getFinishType() == WorkflowMultiInstanceFinishType.PERCENTAGE.getCode()) {
                            Object nrOfInstances = runtimeService.getVariable(task.getExecutionId(), WorkflowConstant.MULTI_INSTANCE_VAR_KEY);

                            //判断是否达到百分比
                            if ((Convert.toDouble(nowAgreeCount) / Convert.toDouble(nrOfInstances) * 100) > Convert.toDouble(userTaskConfig.getCountersignConfig().getPercentage())) {
                                runtimeService.setVariable(task.getProcessInstanceId(), resultVarName, Boolean.TRUE);
                            }

                        }

                    } else {
                        Object rejectObj = runtimeService.getVariable(task.getProcessInstanceId(), rejectButtonVarName);
                        int nowRejectCount = Convert.toInt(rejectObj) + 1;
                        runtimeService.setVariable(task.getProcessInstanceId(), rejectButtonVarName, nowRejectCount);

                        //如果是全部 需要全部同意 只要代码进入这里 就代表  已经 会签不通过
                        if (userTaskConfig.getCountersignConfig().getFinishType() == WorkflowMultiInstanceFinishType.ALL.getCode()) {
                            runtimeService.setVariable(task.getProcessInstanceId(), resultVarName, Boolean.FALSE);
                        }
                    }

                }

                Object resultName = new Object();
                //获取会签流程的结果，防止会签节点后面直接跟结束节点，导致拿不到流程数据
                if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() != WorkflowMultiInstanceType.NONE.getCode()) {
                    resultName = runtimeService.getVariable(task.getProcessInstanceId(), resultVarName);
                }

                //如果是同意  || 拒绝  || 其他  默认都是流程往下走
                if (dto.getApprovedType() == WorkflowApproveType.AGREE.getCode() || dto.getApprovedType() == WorkflowApproveType.DISAGREE.getCode() || dto.getApprovedType() == WorkflowApproveType.OTHER.getCode()) {

                    taskService.complete(task.getId());
                    List<Task> list = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
                    //如果不需要指定审批人 默认走自动同意规则
                    invokeAutoAgree(task.getProcessInstanceId(), workflowSchema.getId(), workflowSchemaConfig, list);

                    //下一个节点的任务名称集合
                    List<String> nextTaskNameList = list.stream().map(Task::getName).collect(Collectors.toList());

                    //批量审批流程信息记录
                    WorkflowRecord record = new WorkflowRecord();
                    record.setNodeId(task.getId());
                    record.setNodeName(task.getName());
                    record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
                    record.setProcessId(task.getProcessInstanceId());
                    record.setSchemaId(schemaId);
                    record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
                    //时间设值必须写在complete上面，防止用户任务节点监听时的流程信息，在次任务前面。
                    record.setRecordTime(LocalDateTime.now());

                    //新增审批详情返回给前端
                    ApproveMultiVo approveMultiVo = new ApproveMultiVo();
                    WorkflowExtra extraFirst = workflowExtras.stream().filter(x -> x.getTaskId().equals(task.getId())).findFirst().orElse(new WorkflowExtra());
                    approveMultiVo.setStartUserName(extraFirst.getStartUserName());
                    approveMultiVo.setSchemaName(workflowSchema.getName());
                    approveMultiVo.setCurrentNodeName(task.getName());
                    if (dto.getApprovedType() == WorkflowApproveType.AGREE.getCode()) {
                        //新增工作流程信息数据
                        addWorkflowRecord(userTaskConfig, nextTaskNameList, user, record, task.getName(), "同意", dto.getApprovedContent(), task, resultName);

                        if (nextTaskNameList.size() == 0) {
                            String message = "【" + user.getName() + "】【同意】审批,审批意见为：“【" + dto.getApprovedContent() + "】”,由【" + task.getName() + "】 流转到【结束节点】";
                            approveMultiVo.setApproveDetail(message);
                        }
                        //单流向
                        if (nextTaskNameList.size() == 1) {
                            String message = "【" + user.getName() + "】【同意】审批,审批意见为：“【" + dto.getApprovedContent() + "】”,由【" + task.getName() + "】 流转到【" + nextTaskNameList.get(0) + "】";
                            approveMultiVo.setApproveDetail(message);
                        }//多流向
                        if (nextTaskNameList.size() > 1) {
                            String message = "【" + user.getName() + "】【同意】审批,审批意见为：“【" + dto.getApprovedContent() + "】”,由【" + task.getName() + "】 流转到【" + nextTaskNameList.get(0) + "】";
                            for (int i = 1; i < nextTaskNameList.size(); i++) {
                                message = message + "、【" + nextTaskNameList.get(i) + "】";
                            }
                            approveMultiVo.setApproveDetail(message);
                        }
                        approveMultiVo.setApproveResult("审核成功");
                    }
                    if (dto.getApprovedType() == WorkflowApproveType.DISAGREE.getCode()) {
                        //新增工作流程信息数据
                        addWorkflowRecord(userTaskConfig, nextTaskNameList, user, record, task.getName(), "拒绝", dto.getApprovedContent(), task, resultName);
                        if (nextTaskNameList.size() == 0) {
                            String message = "【" + user.getName() + "】【拒绝】审批,审批意见为：“【" + dto.getApprovedContent() + "】”,由【" + task.getName() + "】 流转到【结束节点】";
                            approveMultiVo.setApproveDetail(message);
                        }
                        //单流向
                        if (nextTaskNameList.size() == 1) {
                            String message = "【" + user.getName() + "】【拒绝】审批,审批意见为：“【" + dto.getApprovedContent() + "】”,由【" + task.getName() + "】 流转到【" + nextTaskNameList.get(0) + "】";
                            approveMultiVo.setApproveDetail(message);
                        }//多流向
                        if (nextTaskNameList.size() > 1) {
                            String message = "【" + user.getName() + "】【拒绝】审批,审批意见为：“【" + dto.getApprovedContent() + "】”,由【" + task.getName() + "】 流转到【" + nextTaskNameList.get(0) + "】";
                            for (int i = 1; i < nextTaskNameList.size(); i++) {
                                message = message + "、【" + nextTaskNameList.get(i) + "】";
                            }
                            approveMultiVo.setApproveDetail(message);
                        }
                        approveMultiVo.setApproveResult("审核成功");
                    }
                    approveMultiVoList.add(approveMultiVo);
                }
            }

            for (Session session : sessionList) {
                //TODO  如果审批接口 出现Can't call commit when autocommit=true 错误  也可以像这样设置解决
                session.getConnection().setAutoCommit(false);
                session.commit();
                session.close();
            }
        } catch (Exception e) {
            for (Session session : sessionList) {
                session.quietRollback();
            }
            if (e instanceof MyException) {
                throw (MyException) e;
            } else if (e.getMessage().contains("No outgoing sequence flow")) {
                throw new MyException("流转条件错误，请检查流转条件设置!");
            } else {
                throw new MyException("表单提交错误， 请联系系统管理员!");
            }
        }finally {
            for (Session session : sessionList) {
                session.close();
            }
        }


        return approveMultiVoList;
    }

    @Override
    public ApproveMultiInfoVo approveMultiInfo(ApproveMultiInfoDto dto) {
        String[] taskIdArray = dto.getTaskIds().split(StringPool.COMMA);
        List<Task> taskList = taskService.createTaskQuery().taskIdIn(taskIdArray).list();


        List<HistoricVariableInstance> varList = historyService.createHistoricVariableInstanceQuery()
                .processInstanceIdIn(taskList.stream().map(Task::getProcessInstanceId).toArray(String[]::new))
                .variableName(WorkflowConstant.PROCESS_SCHEMA_ID_KEY)
                .list();


        List<Object> allSchemaId = varList.stream().filter(x -> x.getName().equals(WorkflowConstant.PROCESS_SCHEMA_ID_KEY)).map(HistoricVariableInstance::getValue).collect(Collectors.toList());

        //排除xml 查出数据
        List<WorkflowSchema> workflowSchemaList = workflowSchemaMapper.selectList(Wrappers.lambdaQuery(WorkflowSchema.class).in(WorkflowSchema::getId, allSchemaId).select(x -> !x.getProperty().startsWith(DB_FIELD_XML_PREFIX)));

        ApproveMultiInfoVo vo = new ApproveMultiInfoVo();
        for (String taskId : taskIdArray) {
            Task task = taskList.stream().filter(t -> t.getId().equals(taskId)).findFirst().orElse(new TaskEntity());
            HistoricVariableInstance schemaIdInstance = varList.stream().filter(v -> v.getName().equals(WorkflowConstant.PROCESS_SCHEMA_ID_KEY) && v.getProcessInstanceId().equals(task.getProcessInstanceId())).findFirst().orElse(new HistoricVariableInstanceEntity());

            WorkflowSchema workflowSchema = workflowSchemaList.stream().filter(w -> w.getId().equals(Convert.toLong(schemaIdInstance.getValue()))).findFirst().orElse(new WorkflowSchema());
            //获取到整个流程模板的配置
            WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);
            //找到当前用户任务节点的配置信息
            Optional<Map<String, Object>> userTaskMapOptional = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(task.getTaskDefinitionKey())).findFirst();
            //判断是否找得到此任务节点 没有则报错
            if (!userTaskMapOptional.isPresent()) {
                throw new MyException("【任务id ：" + task.getId() + "】 用户任务节点配置有误，请联系管理员！");
            }

            //将map 转为 java类
            UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskMapOptional.get());

            if (userTaskConfig.getOpinionConfig().getEnabled()) {
                vo.setNeedStamp(Boolean.TRUE);//需要电子签章

                if (userTaskConfig.getOpinionConfig().getSignature() == YesOrNoEnum.YES.getCode()) {
                    vo.setNeedPassword(Boolean.TRUE);//需要电子签章密码
                }
                break;
            }

        }

        return vo;
    }


    @Override
    public boolean setApproveUser(ApproveUserDto dto) {
        Object schemaId = taskService.getVariable(dto.getTaskId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY);

        //获取任务信息
        Task task = taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();

        //查询下一个节点审批人 委托 数据  将被委托人加入到 审批人数据中
        LambdaQueryWrapper<WorkflowDelegate> wrapper = Wrappers.lambdaQuery(WorkflowDelegate.class)
                .in(WorkflowDelegate::getUserId, dto.getApprovedUsers())
                .gt(WorkflowDelegate::getStartTime, DateUtil.parse(DateUtil.now()).toJdkDate())
                .lt(WorkflowDelegate::getEndTime, DateUtil.parse(DateUtil.now()).toJdkDate())
                .like(WorkflowDelegate::getSchemaIds, schemaId)
                .select(WorkflowDelegate::getDelegateUserIds);

        List<WorkflowDelegate> workflowDelegates = workflowDelegateMapper.selectList(wrapper);

        if (workflowDelegates.size() > 0) {

            List<String> allDelegateStr = workflowDelegates.stream().map(WorkflowDelegate::getDelegateUserIds).collect(Collectors.toList());

            List<Long> allDelegateUserIds = Arrays.stream(StrUtil.join(StringPool.COMMA, allDelegateStr).split(StringPool.COMMA)).map(Convert::toLong).collect(Collectors.toList());
            dto.getApprovedUsers().addAll(allDelegateUserIds);

            //新增流程发起流程记录
            //获取当前用户的信息
            User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
            //获取用户id对应的用户名
            String allDelegateUserNames = getAllUserNamesByIds(allDelegateUserIds);
            String message = "【" + user.getName() + "】通过【流程委托】委托【" + allDelegateUserNames + "】作为审批人";
            //新增流程发起流程记录
            WorkflowRecord record = new WorkflowRecord();
            record.setRecordTime(LocalDateTime.now());
            addProcessRecord(task, Convert.toLong(schemaId), message, record);
        }
        //包括
        String allUserIds = StrUtil.join(StringPool.COMMA, dto.getApprovedUsers());

        VariableMap variableMap = Variables.createVariables()
                .putValue(WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode())
                .putValue(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, allUserIds);
        taskService.setVariablesLocal(dto.getTaskId(), variableMap);

        //这里不再写 发送消息的逻辑 默认使用一个 redis key 使用redis过期策略 执行发送消息的功能 解耦 偷懒
        //每个消息默认相隔一秒 等于队列一样的逻辑
        redisUtil.set(dto.getTaskId() + StringPool.UNDERSCORE + WorkflowConstant.TASK_IS_APPOINT_APPROVE, dto.getTaskId(), 1);
        return true;
    }

    @Override
    public boolean setApproveUserMulti(ApproveUserMultiDto dto) {
        List<ApproveUserDto> approveUserList = dto.getApproveUserList();
        if (approveUserList == null || approveUserList.size() == 0) {
            throw new MyException("指定审批人不能为空！");
        }

        List<Long> allUser = new ArrayList<>();
        for (ApproveUserDto approveUserDto : approveUserList) {
            allUser.addAll(approveUserDto.getApprovedUsers());

            //指定审批人、流程发起后、需记录指定的人的流转信息
            String approveName = WorkFlowUtil.getAllUserNamesByIds(approveUserDto.getApprovedUsers());
            //查询到对应task的流转信息，将message重新设值，将设置的审批人加入到里面,如果没有直接新增一条，所以需要先查询再修改
            QueryWrapper<WorkflowRecord> wrapper = new QueryWrapper<WorkflowRecord>()
                    .eq("node_id", approveUserDto.getTaskId())
                    .like("message", "准备审批");
            WorkflowRecord records = workflowRecordMapper.selectOne(wrapper);
            if (ObjectUtil.isEmpty(records)) {
                Object schemaId = taskService.getVariable(approveUserDto.getTaskId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY);
                //获取任务信息
                Task task = taskService.createTaskQuery().taskId(approveUserDto.getTaskId()).singleResult();
                //新增流程发起流程记录
                WorkflowRecord record = new WorkflowRecord();
                record.setRecordTime(LocalDateTime.now());
                addProcessRecord(task, Convert.toLong(schemaId), "【" + approveName + "】" + "准备审批", record);
            } else {
                //修改原本的流转记录
                records.setMessage("【" + approveName + "】" + "准备审批");
                workflowRecordMapper.updateById(records);
            }
        }

        //查询下一个节点审批人 委托 数据  将被委托人加入到 审批人数据中
        LambdaQueryWrapper<WorkflowDelegate> wrapper = Wrappers.lambdaQuery(WorkflowDelegate.class)
                .in(WorkflowDelegate::getUserId, allUser)
                .gt(WorkflowDelegate::getStartTime, DateUtil.parse(DateUtil.now()).toJdkDate())
                .lt(WorkflowDelegate::getEndTime, DateUtil.parse(DateUtil.now()).toJdkDate())
                .like(WorkflowDelegate::getSchemaIds, dto.getSchemaId())
                .select(WorkflowDelegate::getDelegateUserIds);

        List<WorkflowDelegate> workflowDelegates = workflowDelegateMapper.selectList(wrapper);

        int i = 1;
        //获取当前用户的信息
        User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
        for (ApproveUserDto item : approveUserList) {

            if (workflowDelegates.size() > 0) {
                List<String> allDelegateStr = workflowDelegates.stream()
                        .filter(d -> item.getApprovedUsers().contains(d.getUserId()))
                        .map(WorkflowDelegate::getDelegateUserIds).collect(Collectors.toList());
                List<Long> allDelegateUserIds = Arrays.stream(StrUtil.join(StringPool.COMMA, allDelegateStr).split(StringPool.COMMA)).map(Convert::toLong).collect(Collectors.toList());
                item.getApprovedUsers().addAll(allDelegateUserIds);

                //新增流程发起流程记录
                //获取用户id对应的用户名
                String allDelegateUserNames = getAllUserNamesByIds(allDelegateUserIds);
                String message = "【" + user.getName() + "】通过【流程委托】委托【" + allDelegateUserNames + "】作为审批人";
                //获取任务信息
                Task task = taskService.createTaskQuery().taskId(item.getTaskId()).singleResult();
                //新增流程发起流程记录
                WorkflowRecord record = new WorkflowRecord();
                record.setRecordTime(LocalDateTime.now());
                addProcessRecord(task, Convert.toLong(dto.getSchemaId()), message, record);
            }
            String userIds = StrUtil.join(StringPool.COMMA, item.getApprovedUsers());

            VariableMap variableMap = Variables.createVariables()
                    .putValue(WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode())
                    .putValue(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, userIds);

            taskService.setVariablesLocal(item.getTaskId(), variableMap);
            //到期未指定的任务 将变量设置为不需要指定
            taskService.setVariableLocal(item.getTaskId(), WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode());

            //这里不再写 发送消息的逻辑 默认使用一个 redis key 使用redis过期策略 执行发送消息的功能 解耦 偷懒
            //每个消息默认相隔一秒 等于队列一样的逻辑
            redisUtil.set(item.getTaskId() + StringPool.UNDERSCORE + WorkflowConstant.TASK_IS_APPOINT_APPROVE, item.getTaskId(), i);
            i++;
        }
        return true;
    }

    @Override
    public PageOutput<RelationTaskPageVo> getRelationTaskPage(RelationTaskPageDto dto) {
        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, dto.getSchemaId()).select(x -> !x.getProperty().startsWith(DB_FIELD_XML_PREFIX)));

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);

        List<RelationProcessConfig> relationProcessConfigList = workflowSchemaConfig.getProcessConfig().getRelationProcessConfigs();

        //获取到当前关联任务的设置  判断是 审批中 还是审批结束
        Optional<RelationProcessConfig> thisRelationProcessOp = relationProcessConfigList.stream().filter(x -> x.getId().equals(dto.getRelationSchemaId())).findFirst();

        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .processVariableValueEquals(WorkflowConstant.PROCESS_SCHEMA_ID_KEY, dto.getRelationSchemaId());


        //
        if (dto.getStartTime() != null) {
            historicTaskInstanceQuery.startedAfter(dto.getStartTime());
        }
        if (dto.getEndTime() != null) {
            historicTaskInstanceQuery.startedBefore(dto.getEndTime());
        }
        if (StrUtil.isNotBlank(dto.getKeyword())) {
            historicTaskInstanceQuery.processVariableValueLike(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY, StringPool.PERCENT + dto.getKeyword() + StringPool.PERCENT);
        }

        if (thisRelationProcessOp.isPresent()) {
            //如果是需要已经审批完成的
            if (thisRelationProcessOp.get().getProcessStatus() == YesOrNoEnum.YES.getCode()) {
                historicTaskInstanceQuery.finished();
            } else {
                historicTaskInstanceQuery.unfinished();
            }
        }

        //如果是查询发起人自己的
        Optional<RelationProcessConfig> relationProcessConfigOptional = relationProcessConfigList.stream().filter(x -> x.getId().equals(dto.getRelationSchemaId())).findFirst();

        relationProcessConfigOptional.ifPresent(config -> {
            if (config.getProcessAuth() == WorkflowRelationAuthType.ORIGINATOR.getCode()) {
                historicTaskInstanceQuery.processVariableValueEquals(WorkflowConstant.PROCESS_START_USER_ID_KEY, StpUtil.getLoginIdAsLong());
            }
        });
//        for (RelationProcessConfig relationProcessConfig : relationProcessConfigList) {
//            if (relationProcessConfig.getProcessAuth() == WorkflowRelationAuthType.ORIGINATOR.getCode()) {
//                taskQuery.processVariableValueEquals(WorkflowConstant.PROCESS_START_USER_ID_KEY, StpUtil.getLoginId());
//            }
//        }

        long count = historicTaskInstanceQuery.count();
        List<HistoricTaskInstance> historicTaskInstances = historicTaskInstanceQuery.orderByHistoricActivityInstanceStartTime().desc().listPage(Convert.toInt((dto.getLimit() - 1) * dto.getSize()), dto.getSize());
//        List<Task> tasks = taskQuery.listPage(Convert.toInt((dto.getLimit() - 1) * dto.getSize()), dto.getSize());

        List<RelationTaskPageVo> result = new ArrayList<>(historicTaskInstances.size());
        //获取到所有taskid  去extra表一次性查出数据
        List<String> taskIds = historicTaskInstances.stream().map(HistoricTaskInstance::getId).collect(Collectors.toList());


        if (taskIds.size() > 0) {

            List<WorkflowExtra> workflowExtras = workflowExtraMapper.selectList(Wrappers.lambdaQuery(WorkflowExtra.class).in(WorkflowExtra::getTaskId, taskIds));

            for (HistoricTaskInstance task : historicTaskInstances) {
                RelationTaskPageVo vo = new RelationTaskPageVo();
                Optional<WorkflowExtra> extra = workflowExtras.stream().filter(x -> x.getTaskId().equals(task.getId())).findFirst();
                extra.ifPresent(x -> {
                    vo.setSchemaName(x.getSchemaName());
                    vo.setOriginator(x.getStartUserName());
                });
                vo.setProcessId(task.getProcessInstanceId());
                vo.setTaskName(task.getName());
                vo.setTaskId(task.getId());
                vo.setCreateTime(task.getStartTime());
                result.add(vo);
            }
        }

        PageOutput<RelationTaskPageVo> output = new PageOutput<>();
        output.setCurrentPage(dto.getLimit());
        output.setPageSize(dto.getSize());
        output.setTotal(Convert.toInt(count));
        output.setList(result);
        return output;
    }


    @Override
    public RelationTaskInfoVo getRelationTaskInfo(RelationTaskInfoDto dto) {
        RelationTaskInfoVo vo = new RelationTaskInfoVo();

        HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().taskId(dto.getTaskId()).singleResult();

//        Task task = taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();
        HistoricVariableInstance schemaIdVar = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .variableName(WorkflowConstant.PROCESS_SCHEMA_ID_KEY).singleResult();
//        Object schemaId = taskService.getVariable(dto.getTaskId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY);

        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, schemaIdVar.getValue()));

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);

        Optional<Map<String, Object>> userNodeConfigOp = workflowSchemaConfig.getChildNodeConfig().stream().filter(c -> c.containsValue(task.getTaskDefinitionKey())).findFirst();

        if (!userNodeConfigOp.isPresent()) {
            throw new MyException("当前任务没有开始节点");
        }

        List<ProcessRecordListVo> recordListVos = getProcessRecordListVos(task.getProcessInstanceId(), 0);
        vo.setTaskRecords(recordListVos);

        //将map 转为 java类
        UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userNodeConfigOp.get());

        List<Long> formIds = userTaskConfig.getFormConfigs().stream().map(FormConfig::getFormId).collect(Collectors.toList());
        String[] allVarKey = userTaskConfig.getFormConfigs().stream().map(FormConfig::getKey).toArray(String[]::new);

        List<FormTemplate> formTemplates = formTemplateMapper.selectList(Wrappers.lambdaQuery(FormTemplate.class).in(formIds.size() > 0, FormTemplate::getId, formIds));

        List<HistoricVariableInstance> formDataList = historyService.createHistoricVariableInstanceQuery().processInstanceId(task.getProcessInstanceId()).variableNameIn(allVarKey).list();
//        Map<String, Object> allFormData = taskService.getVariables(dto.getTaskId(), allVarKey);

        List<RelationFormInfoVo> formInfoVos = new ArrayList<>(userTaskConfig.getFormConfigs().size());
        for (FormConfig formConfig : userTaskConfig.getFormConfigs()) {

            RelationFormInfoVo formInfoVo = new RelationFormInfoVo();
            formInfoVo.setFormType(formConfig.getFormType());
            formInfoVo.setFormConfig(formConfig);

            //TODO 系统表单 和 自定义表单 走同一套流程  都是使用json  以后看能否有更好的解决方案 解决用户手动修改系统表单代码
            if (formConfig.getFormType() == FormTemplateType.CUSTOM.getCode() || formConfig.getFormType() == FormTemplateType.SYSTEM.getCode()) {
                Optional<FormTemplate> templateOp = formTemplates.stream().filter(f -> f.getId().equals(formConfig.getFormId())).findFirst();
                templateOp.ifPresent(t -> formInfoVo.setFormJson(t.getFormJson()));

                Optional<HistoricVariableInstance> thisFormData = formDataList.stream().filter(x -> x.getName().equals(formConfig.getKey())).findFirst();

                thisFormData.ifPresent(form -> {
                    Map<String, Object> formData = Convert.toMap(String.class, Object.class, form.getValue());
                    formInfoVo.setFormData(formData);
                });

            }
            formInfoVos.add(formInfoVo);
        }
        vo.setFormInfos(formInfoVos);
        vo.setSchemaInfo(BeanUtil.toBean(workflowSchema, WorkflowSchemaInfoVo.class));

        return vo;
    }

    @Override
    public TaskInfoVo getTaskInfo(String taskId) {
        TaskInfoVo vo = new TaskInfoVo();

        HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();

        WorkflowExtra extra = extraService.getOne(Wrappers.lambdaQuery(WorkflowExtra.class).eq(WorkflowExtra::getTaskId, taskId).select(WorkflowExtra::getSchemaId));

//        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
//        Object schemaId = taskService.getVariable(taskId, WorkflowConstant.PROCESS_SCHEMA_ID_KEY);

        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, extra.getSchemaId()));
//        WorkflowSchema workflowSchema = redisUtil.get(WorkflowConstant.SCHEMA_CACHE_PREFIX + extra.getSchemaId(), WorkflowSchema.class);

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);

        Optional<Map<String, Object>> userNodeConfigOp = workflowSchemaConfig.getChildNodeConfig().stream().filter(c -> c.containsValue(task.getTaskDefinitionKey())).findFirst();

        if (!userNodeConfigOp.isPresent()) {
            throw new MyException("当前任务没有开始节点");
        }
        List<ProcessRecordListVo> recordListVos = getProcessRecordListVos(task.getProcessInstanceId(), 0);
        vo.setTaskRecords(recordListVos);

        //查询任务关联任务
        vo.setRelationTaskInfo(getRelationTaskVos(task.getProcessInstanceId()));

        //将map 转为 java类
        UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userNodeConfigOp.get());

        List<Long> formIds = userTaskConfig.getFormConfigs().stream().map(FormConfig::getFormId).collect(Collectors.toList());
        String[] allVarKey = userTaskConfig.getFormConfigs().stream().map(FormConfig::getKey).toArray(String[]::new);
        List<FormTemplate> formTemplates = formTemplateMapper.selectList(Wrappers.lambdaQuery(FormTemplate.class).in(formIds.size() > 0, FormTemplate::getId, formIds));

        List<HistoricVariableInstance> list = historyService.createHistoricVariableInstanceQuery().variableNameIn(allVarKey).list();
//        Map<String, Object> allFormData = taskService.getVariables(taskId, allVarKey);

        List<RelationFormInfoVo> formInfoVos = new ArrayList<>(userTaskConfig.getFormConfigs().size());
        for (FormConfig formConfig : userTaskConfig.getFormConfigs()) {

            RelationFormInfoVo formInfoVo = new RelationFormInfoVo();
            formInfoVo.setFormType(formConfig.getFormType());
            formInfoVo.setFormConfig(formConfig);

            //TODO 系统表单 和 自定义表单 走同一套流程  都是使用json  以后看能否有更好的解决方案 解决用户手动修改系统表单代码
            if (formConfig.getFormType() == FormTemplateType.CUSTOM.getCode() || formConfig.getFormType() == FormTemplateType.SYSTEM.getCode()) {
                Optional<FormTemplate> templateOp = formTemplates.stream().filter(f -> Objects.equals(f.getId(), formConfig.getFormId())).findFirst();
                templateOp.ifPresent(t -> formInfoVo.setFormJson(t.getFormJson()));

                Optional<HistoricVariableInstance> thisFormData = list.stream().filter(x -> x.getName().equals(formConfig.getKey())).findFirst();

                thisFormData.ifPresent(data -> {
                    Map<String, Object> formData = Convert.toMap(String.class, Object.class, data.getValue());
                    formInfoVo.setFormData(formData);
                });

            }

            formInfoVos.add(formInfoVo);

        }
        //如果是多实例的
        if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() != WorkflowMultiInstanceType.NONE.getCode()) {
            vo.setIsAddOrSubSign(userTaskConfig.getCountersignConfig().getAddOrRemove());
        }
        vo.setFormInfos(formInfoVos);
        vo.setSchemaInfo(BeanUtil.toBean(workflowSchema, WorkflowSchemaInfoVo.class));

        vo.setOpinionConfig(userTaskConfig.getOpinionConfig());

        //如果是多实例的
        if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() != WorkflowMultiInstanceType.NONE.getCode()) {
            vo.setIsAddOrSubSign(userTaskConfig.getCountersignConfig().getAddOrRemove());
        }

        if (userTaskConfig.getOpinionConfig().getEnabled()) {
            MPJLambdaWrapper<WorkflowApproveRecord> workflowApproveRecordMPJLambdaWrapper = MPJWrappers.<WorkflowApproveRecord>lambdaJoin()
                    .disableSubLogicDel()
                    .eq(WorkflowApproveRecord::getProcessId, task.getProcessInstanceId())
                    .select(WorkflowApproveRecord::getId)
                    .select(WorkflowApproveRecord.class, x -> VoToColumnUtil.fieldsToColumns(WorkflowApproveRecord.class).contains(x.getProperty()))
                    .selectAs(User::getName, WorkflowApproveRecord::getApproveUserName)
                    .leftJoin(User.class, User::getId, WorkflowApproveRecord::getApproveUserId)
                    .selectAs(Stamp::getFileUrl, WorkflowApproveRecord::getApproveStampUrl)
                    .leftJoin(Stamp.class, Stamp::getId, WorkflowApproveRecord::getApproveStamp);

            List<WorkflowApproveRecord> approveRecords = approveRecordService.selectJoinList(WorkflowApproveRecord.class, workflowApproveRecordMPJLambdaWrapper);

            //取时间最大值 不好取  目前还是全部查出来 然后根据时间 倒序 取第一条数据
            if (userTaskConfig.getOpinionConfig().getShowType() == 1 && approveRecords.size() > 0) {
                WorkflowApproveRecord approveRecord = approveRecords.stream().sorted(Comparator.comparing(WorkflowApproveRecord::getApproveTime).reversed()).collect(Collectors.toList()).get(0);
                List<WorkflowApproveRecord> onlyOne = new ArrayList<>();
                onlyOne.add(approveRecord);
                vo.setTaskApproveOpinions(onlyOne);
            } else {
                vo.setTaskApproveOpinions(approveRecords);
            }
        }
        return vo;
    }

    @Override
    public ProcessInfoVo getProcessInfo(String processId) {
        ProcessInfoVo vo = new ProcessInfoVo();

        HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(processId).variableName(WorkflowConstant.PROCESS_SCHEMA_ID_KEY).singleResult();

        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, historicVariableInstance.getValue()));
//        WorkflowSchema workflowSchema = redisUtil.get(WorkflowConstant.SCHEMA_CACHE_PREFIX + extra.getSchemaId(), WorkflowSchema.class);

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);


        List<ProcessRecordListVo> recordListVos = getProcessRecordListVos(processId, 0);
        vo.setTaskRecords(recordListVos);

        List<Object> allFormConfigs = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsKey("formConfigs")).map(x -> x.get("formConfigs")).collect(Collectors.toList());
        List<FormConfig> formConfigs = new ArrayList<>();


        for (Object allFormConfig : allFormConfigs) {
            List<FormConfig> thisNodeFormConfigs = Convert.toList(FormConfig.class, allFormConfig);

            for (FormConfig thisNodeFormConfig : thisNodeFormConfigs) {
                if (formConfigs.stream().noneMatch(x -> x.getKey().equals(thisNodeFormConfig.getKey()))) {

                    formConfigs.add(thisNodeFormConfig);

                }
            }
        }


        String[] allFormKey = formConfigs.stream().map(FormConfig::getKey).toArray(String[]::new);

        List<Long> formIds = formConfigs.stream().map(FormConfig::getFormId).collect(Collectors.toList());
        List<FormTemplate> formTemplates = formTemplateMapper.selectList(Wrappers.lambdaQuery(FormTemplate.class).in(formIds.size() > 0, FormTemplate::getId, formIds));


        List<HistoricVariableInstance> list = historyService.createHistoricVariableInstanceQuery().processInstanceId(processId).variableNameIn(allFormKey).list();
//        Map<String, Object> allFormData = taskService.getVariables(taskId, allVarKey);

        List<RelationFormInfoVo> formInfoVos = new ArrayList<>(allFormKey.length);
        for (FormConfig formConfig : formConfigs) {

            RelationFormInfoVo formInfoVo = new RelationFormInfoVo();
            formInfoVo.setFormType(formConfig.getFormType());
            formInfoVo.setFormConfig(formConfig);

            if (formConfig.getFormType() == FormTemplateType.CUSTOM.getCode() || formConfig.getFormType() == FormTemplateType.SYSTEM.getCode()) {
                Optional<FormTemplate> templateOp = formTemplates.stream().filter(f -> Objects.equals(f.getId(), formConfig.getFormId())).findFirst();
                templateOp.ifPresent(t -> formInfoVo.setFormJson(t.getFormJson()));

                Optional<HistoricVariableInstance> thisFormData = list.stream().filter(x -> x.getName().equals(formConfig.getKey())).findFirst();

                thisFormData.ifPresent(data -> {
                    Map<String, Object> formData = Convert.toMap(String.class, Object.class, data.getValue());
                    formInfoVo.setFormData(formData);
                });

            }

            formInfoVos.add(formInfoVo);

        }

        vo.setFormInfos(formInfoVos);
        vo.setSchemaInfo(BeanUtil.toBean(workflowSchema, WorkflowSchemaInfoVo.class));
        vo.setWorkflowChat(workflowSchema.getWorkflowChat());

        return vo;
    }

    @Override
    public TaskInfoVo getCirculatedTaskInfo(String taskId) {
        TaskInfoVo vo = new TaskInfoVo();

        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        WorkflowExtra extra = extraService.getOne(Wrappers.lambdaQuery(WorkflowExtra.class).eq(WorkflowExtra::getTaskId, taskId).select(WorkflowExtra::getSchemaId));
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, extra.getSchemaId()));

//        WorkflowSchema workflowSchema = redisUtil.get(WorkflowConstant.SCHEMA_CACHE_PREFIX + extra.getSchemaId(), WorkflowSchema.class);

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);

        Optional<Map<String, Object>> userNodeConfigOp = workflowSchemaConfig.getChildNodeConfig().stream().filter(c -> c.containsValue(historicTaskInstance.getTaskDefinitionKey())).findFirst();

        if (!userNodeConfigOp.isPresent()) {
            throw new MyException("当前任务没有开始节点");
        }
        List<ProcessRecordListVo> recordListVos = getProcessRecordListVos(historicTaskInstance.getProcessInstanceId(), 0);
        vo.setTaskRecords(recordListVos);

        //查询任务关联任务
        vo.setRelationTaskInfo(getRelationTaskVos(historicTaskInstance.getId()));

        //将map 转为 java类
        UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userNodeConfigOp.get());

        List<Long> formIds = userTaskConfig.getFormConfigs().stream().map(FormConfig::getFormId).collect(Collectors.toList());
        String[] allVarKey = userTaskConfig.getFormConfigs().stream().map(FormConfig::getKey).toArray(String[]::new);
        List<FormTemplate> formTemplates = formTemplateMapper.selectList(Wrappers.lambdaQuery(FormTemplate.class).in(formIds.size() > 0, FormTemplate::getId, formIds));

        List<HistoricVariableInstance> list = historyService.createHistoricVariableInstanceQuery().variableNameIn(allVarKey).list();

        List<RelationFormInfoVo> formInfoVos = new ArrayList<>(userTaskConfig.getFormConfigs().size());
        for (FormConfig formConfig : userTaskConfig.getFormConfigs()) {

            RelationFormInfoVo formInfoVo = new RelationFormInfoVo();
            formInfoVo.setFormType(formConfig.getFormType());
            formInfoVo.setFormConfig(formConfig);

            //TODO 系统表单 和 自定义表单 走同一套流程  都是使用json  以后看能否有更好的解决方案 解决用户手动修改系统表单代码
            if (formConfig.getFormType() == FormTemplateType.CUSTOM.getCode() || formConfig.getFormType() == FormTemplateType.SYSTEM.getCode()) {
                Optional<FormTemplate> templateOp = formTemplates.stream().filter(f -> Objects.equals(f.getId(), formConfig.getFormId())).findFirst();
                templateOp.ifPresent(t -> formInfoVo.setFormJson(t.getFormJson()));

                Optional<HistoricVariableInstance> thisFormData = list.stream().filter(x -> x.getName().equals(formConfig.getKey())).findFirst();

                thisFormData.ifPresent(data -> {
                    Map<String, Object> formData = Convert.toMap(String.class, Object.class, data.getValue());
                    formInfoVo.setFormData(formData);
                });

            }

            formInfoVos.add(formInfoVo);

        }
        vo.setFormInfos(formInfoVos);
        vo.setSchemaInfo(BeanUtil.toBean(workflowSchema, WorkflowSchemaInfoVo.class));

        LambdaQueryWrapper<WorkflowCirculated> queryWrapper = Wrappers.lambdaQuery(WorkflowCirculated.class)
                .eq(WorkflowCirculated::getTaskId, taskId)
                .eq(WorkflowCirculated::getCirculatedUserId, StpUtil.getLoginIdAsLong())
                .select(WorkflowCirculated::getId);

        WorkflowCirculated thisCirculated = circulatedService.getOne(queryWrapper);
        thisCirculated.setIsRead(YesOrNoEnum.YES.getCode());
        circulatedService.updateById(thisCirculated);

        return vo;
    }

    /**
     * 根据任务id 查询关联任务信息
     *
     * @param processId
     * @return
     */
    private List<TaskInfoRelationTaskVo> getRelationTaskVos(String processId) {
        List<TaskInfoRelationTaskVo> relationTaskVos = new ArrayList<>();
        List<HistoricVariableInstance> list = historyService.createHistoricVariableInstanceQuery().processInstanceId(processId).variableName(WorkflowConstant.RELATION_TASK_KEY).list();

        if (list != null && list.size() > 0) {
            List<Object> allRelationTaskInfo = list.stream().map(HistoricVariableInstance::getValue).collect(Collectors.toList());
            List<LaunchRelationTaskDto> launchRelationTaskDtos = Convert.toList(LaunchRelationTaskDto.class, allRelationTaskInfo);

            List<String> allTaskId = launchRelationTaskDtos.stream().map(LaunchRelationTaskDto::getTaskId).collect(Collectors.toList());

            List<WorkflowExtra> allRelationTask = extraService.list(Wrappers.lambdaQuery(WorkflowExtra.class).in(WorkflowExtra::getTaskId, allTaskId));

            for (WorkflowExtra extra : allRelationTask) {
                TaskInfoRelationTaskVo relationTaskVo = new TaskInfoRelationTaskVo();
                relationTaskVo.setTaskId(extra.getTaskId());
                relationTaskVo.setTaskName(extra.getTaskName());
                relationTaskVos.add(relationTaskVo);
            }
        }
        return relationTaskVos;
    }

    @Override
    public FinishedTaskVo getFinishedTask(String processInstanceId) {
        FinishedTaskVo vo = new FinishedTaskVo();
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (historicProcessInstance == null) {
            throw new MyException("流程id不存在！");
        }
        List<HistoricActivityInstance> activityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();

        BpmnModelInstance bpmnModelInstance = repositoryService.getBpmnModelInstance(activityInstanceList.get(0).getProcessDefinitionId());

        List<String> finishedActivity = activityInstanceList.stream().filter(ins -> !ins.isCanceled()).map(ins -> {
            if (ins.getActivityId().contains(WorkflowConstant.MULTI_INSTANCE_STRING)) {
                return StrUtil.replace(ins.getActivityId(), WorkflowConstant.MULTI_INSTANCE_STRING, StringPool.EMPTY);
            } else {
                return ins.getActivityId();
            }
        }).collect(Collectors.toList());

        List<String> outgoingList = new ArrayList<>();
        for (HistoricActivityInstance historicActivityInstance : activityInstanceList) {//先保存所有已走过节点的outgoing数据
            ModelElementInstance modelElementById = bpmnModelInstance.getModelElementById(historicActivityInstance.getActivityId());

            //如果包含了多实例的关键字
            if (historicActivityInstance.getActivityId().contains(WorkflowConstant.MULTI_INSTANCE_STRING)) {
                String replace = StrUtil.replace(historicActivityInstance.getActivityId(), WorkflowConstant.MULTI_INSTANCE_STRING, StringPool.EMPTY);
                modelElementById = bpmnModelInstance.getModelElementById(replace);
            }
            Collection<SequenceFlow> outgoing = ((FlowNodeImpl) modelElementById).getOutgoing();
            if (outgoing != null && outgoing.size() > 0) {
                for (SequenceFlow sequenceFlow : outgoing) {
                    outgoingList.add(sequenceFlow.getId());
                }
            }
        }

        for (HistoricActivityInstance historicActivityInstance : activityInstanceList) {
            ModelElementInstance modelElementById = bpmnModelInstance.getModelElementById(historicActivityInstance.getActivityId());

            //如果包含了多实例的关键字
            if (historicActivityInstance.getActivityId().contains(WorkflowConstant.MULTI_INSTANCE_STRING)) {
                String replace = StrUtil.replace(historicActivityInstance.getActivityId(), WorkflowConstant.MULTI_INSTANCE_STRING, StringPool.EMPTY);
                modelElementById = bpmnModelInstance.getModelElementById(replace);
            }
            Collection<SequenceFlow> incoming = ((FlowNodeImpl) modelElementById).getIncoming();

            if (incoming != null && incoming.size() > 0) {
                if (incoming.size() > 1) {
                    //查询已经走过的节点的outgoing数据
                    if (CollectionUtil.isNotEmpty(outgoingList)) {
                        for (SequenceFlow sequenceFlow : incoming) {
                            if (outgoingList.contains(sequenceFlow.getId())) {
                                finishedActivity.add(sequenceFlow.getId());
                            }
                        }
                    }
                } else {
                    for (SequenceFlow sequenceFlow : incoming) {
                        finishedActivity.add(sequenceFlow.getId());
                    }
                }
            }
        }
        vo.setFinishedNodes(finishedActivity);
        //如果已经流程结束  就代表没有当前节点
        if (historicProcessInstance.getEndTime() != null) {
            return vo;
        }
        List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        List<String> currentTask = list.stream().map(Task::getTaskDefinitionKey).distinct().collect(Collectors.toList());
        vo.setCurrentNodes(currentTask);

        return vo;
    }

    @Override
    public FormFinishedTaskVo getFormFinishedTask(FormFinishedTaskDto dto) {

        FormFinishedTaskVo vo = new FormFinishedTaskVo();
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getFormId, dto.getFormId()));

        vo.setXmlContent(workflowSchema.getXmlContent());

        WorkflowFormRelation formRelation = formRelationService.getOne(Wrappers.lambdaQuery(WorkflowFormRelation.class).eq(WorkflowFormRelation::getFormId, dto.getFormId()).eq(WorkflowFormRelation::getFormKeyValue, dto.getKeyValue()));

        //如果包含 表单数据 主键值  需要返回 所有已经完成的流程节点内容
        if (StrUtil.isNotBlank(dto.getKeyValue())) {
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(formRelation.getProcessId()).singleResult();
            if (historicProcessInstance == null) {
                throw new MyException("流程id不存在！");
            }
            List<HistoricActivityInstance> activityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(formRelation.getProcessId()).list();

            BpmnModelInstance bpmnModelInstance = repositoryService.getBpmnModelInstance(activityInstanceList.get(0).getProcessDefinitionId());

            List<String> finishedActivity = activityInstanceList.stream().filter(ins -> !ins.isCanceled()).map(ins -> {
                if (ins.getActivityId().contains(WorkflowConstant.MULTI_INSTANCE_STRING)) {
                    return StrUtil.replace(ins.getActivityId(), WorkflowConstant.MULTI_INSTANCE_STRING, StringPool.EMPTY);
                } else {
                    return ins.getActivityId();
                }
            }).collect(Collectors.toList());

            for (HistoricActivityInstance historicActivityInstance : activityInstanceList) {
                ModelElementInstance modelElementById = bpmnModelInstance.getModelElementById(historicActivityInstance.getActivityId());

                //如果包含了多实例的关键字
                if (historicActivityInstance.getActivityId().contains(WorkflowConstant.MULTI_INSTANCE_STRING)) {
                    String replace = StrUtil.replace(historicActivityInstance.getActivityId(), WorkflowConstant.MULTI_INSTANCE_STRING, StringPool.EMPTY);
                    modelElementById = bpmnModelInstance.getModelElementById(replace);
                }
                Collection<SequenceFlow> incoming = ((FlowNodeImpl) modelElementById).getIncoming();
                if (incoming != null && incoming.size() > 0) {
                    for (SequenceFlow sequenceFlow : incoming) {
                        finishedActivity.add(sequenceFlow.getId());
                    }
                }
            }
            vo.setFinishedNodes(finishedActivity);
            //如果已经流程结束  就代表没有当前节点
            if (historicProcessInstance.getEndTime() != null) {
                return vo;
            }
            List<Task> list = taskService.createTaskQuery().processInstanceId(formRelation.getProcessId()).list();
            List<String> currentTask = list.stream().map(Task::getTaskDefinitionKey).distinct().collect(Collectors.toList());
            vo.setCurrentNodes(currentTask);
        }

        return vo;
    }

    @Override
    public List<ProcessRecordListVo> getProcessRecord(String processInstanceId) {
        return getProcessRecordListVos(processInstanceId, 0);
    }

    @Override
    public PageOutput<CirculatedTaskPageVo> getCirculatedTaskPage(CirculatedTaskPageDto dto) {

        if (ObjectUtil.isNotNull(dto.getStartTime()) && ObjectUtil.isNotNull(dto.getEndTime())) {
            dto.setStartTime(WorkFlowUtil.getStartOfDay(dto.getStartTime()));
            dto.setEndTime(WorkFlowUtil.getEndOfDay(dto.getEndTime()));
        }

        //因为多表关联 会有多个表都使用了id字段，  所以必须专门指定主表的Id
        IPage<CirculatedTaskPageVo> page = circulatedService.selectJoinListPage(ConventPage.getPage(dto), CirculatedTaskPageVo.class,
                MPJWrappers.<WorkflowCirculated>lambdaJoin()
                        .orderByDesc(WorkflowCirculated::getId)
                        .like(StrUtil.isNotBlank(dto.getTaskName()), WorkflowCirculated::getTaskName, dto.getTaskName())
                        .like(StrUtil.isNotBlank(dto.getName()), WorkflowSchema::getName, dto.getName())
                        .like(StrUtil.isNotBlank(dto.getKeyword()), WorkflowSchema::getName, dto.getKeyword())
                        .eq(StrUtil.isNotBlank(dto.getOriginator()), User::getId, dto.getOriginator())
                        .eq(StrUtil.isNotBlank(dto.getSerialNumber()), WorkflowCirculated::getSerialNumber, dto.getSerialNumber())
                        .between(ObjectUtil.isNotNull(dto.getStartTime()) && ObjectUtil.isNotNull(dto.getEndTime()), WorkflowCirculated::getCreateTime, dto.getStartTime(), dto.getEndTime())
                        .eq(WorkflowCirculated::getCirculatedUserId, StpUtil.getLoginIdAsLong())
                        .select(WorkflowCirculated::getId)
                        .select(WorkflowCirculated.class, x -> VoToColumnUtil.fieldsToColumns(CirculatedTaskPageVo.class).contains(x.getProperty()))
                        .selectAs(WorkflowCirculated::getTaskName, CirculatedTaskPageVo::getCurrentTaskName)
                        .selectAs(WorkflowSchema::getName, CirculatedTaskPageVo::getSchemaName)
                        .selectAs(User::getName, CirculatedTaskPageVo::getOriginator)
                        .leftJoin(User.class, User::getId, WorkflowCirculated::getStartUserId)
                        .leftJoin(WorkflowSchema.class, WorkflowSchema::getId, WorkflowCirculated::getSchemaId));

        String[] taskIds = page.getRecords().stream().map(CirculatedTaskPageVo::getTaskId).toArray(String[]::new);
        List<Task> list = taskService.createTaskQuery().taskIdIn(taskIds).list();
        for (CirculatedTaskPageVo record : page.getRecords()) {
            Optional<Task> thisTask = list.stream().filter(x -> x.getId().equals(record.getTaskId())).findFirst();
            if (thisTask.isPresent()) {
                if (thisTask.get().isSuspended()) {
                    record.setStatus(2);
                } else {
                    record.setStatus(0);
                }
            } else {
                record.setStatus(1);
            }

        }

        return ConventPage.getPageOutput(page);
    }

    @Override
    public PageOutput<FinishedTaskPageVo> getFinishedTaskPage(FinishedTaskPageDto dto) {

//        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
//                .taskVariableValueLike(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StringPool.PERCENT + StpUtil.getLoginIdAsLong() + StringPool.PERCENT)
//                .list();
//        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
//                .processInstanceId(dto.getProcessId())
//                .activityType(WorkflowConstant.BPMN_USER_TASK_TYPE_NAME)
//                .finished()
//                .orderByHistoricActivityInstanceEndTime()
//                .asc()
//                .list();
//

        if (ObjectUtil.isNotNull(dto.getStartTime()) && ObjectUtil.isNotNull(dto.getEndTime())) {
            dto.setStartTime(WorkFlowUtil.getStartOfDay(dto.getStartTime()));
            dto.setEndTime(WorkFlowUtil.getEndOfDay(dto.getEndTime()));
        }

        //因为多表关联 会有多个表都使用了id字段，  所以必须专门指定主表的Id
        IPage<FinishedTaskPageVo> page = approveRecordService.selectJoinListPage(ConventPage.getPage(dto), FinishedTaskPageVo.class,
                MPJWrappers.<WorkflowApproveRecord>lambdaJoin()
                        .disableSubLogicDel()
                        .distinct()
                        .orderByDesc(WorkflowApproveRecord::getId)
                        .like(StrUtil.isNotBlank(dto.getTaskName()), WorkflowCirculated::getTaskName, dto.getTaskName())
                        .like(StrUtil.isNotBlank(dto.getName()), WorkflowSchema::getName, dto.getName())
                        .like(StrUtil.isNotBlank(dto.getKeyword()), WorkflowSchema::getName, dto.getKeyword())
                        .eq(StrUtil.isNotBlank(dto.getOriginator()), WorkflowExtra::getStartUserId, dto.getOriginator())
                        .eq(StrUtil.isNotBlank(dto.getSerialNumber()), WorkflowCirculated::getSerialNumber, dto.getSerialNumber())
                        .between(ObjectUtil.isNotNull(dto.getStartTime()) && ObjectUtil.isNotNull(dto.getEndTime()), WorkflowExtra::getLaunchTime, dto.getStartTime(), dto.getEndTime())
                        .eq(WorkflowApproveRecord::getApproveUserId, StpUtil.getLoginIdAsLong())
                        .select(WorkflowApproveRecord::getId)
                        .select(WorkflowApproveRecord.class, x -> VoToColumnUtil.fieldsToColumns(FinishedTaskPageVo.class).contains(x.getProperty()))
                        .selectAs(WorkflowSchema::getName, FinishedTaskPageVo::getSchemaName)
                        .selectAs(WorkflowExtra::getCurrentProgress, FinishedTaskPageVo::getCurrentProgress)
                        .selectAs(WorkflowExtra::getTaskName, FinishedTaskPageVo::getCurrentTaskName)
                        .selectAs(WorkflowExtra::getLaunchTime, FinishedTaskPageVo::getCreateTime)
                        .selectAs(WorkflowExtra::getProcessName, FinishedTaskPageVo::getProcessName)
                        .selectAs(User::getName, FinishedTaskPageVo::getOriginator)
                        .leftJoin(User.class, User::getId, WorkflowApproveRecord::getStartUserId)
                        .leftJoin(WorkflowExtra.class, WorkflowExtra::getTaskId, WorkflowApproveRecord::getTaskId)
                        .leftJoin(WorkflowSchema.class, WorkflowSchema::getId, WorkflowApproveRecord::getSchemaId));

        return ConventPage.getPageOutput(page);

    }


    @Override
    public PageOutput<MyProcessPageVo> getMyProcessPage(MyProcessPageDto dto) {
        User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());

        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
                .variableValueEquals(WorkflowConstant.PROCESS_START_USER_ID_KEY, StpUtil.getLoginIdAsLong())
                .variableValueEquals(WorkflowConstant.PROCESS_ISRECYCLE_FLAG_KEY, WorkflowIsRecycleType.NO.getCode());

        if (StrUtil.isNotBlank(dto.getSerialNumber()) && StrUtil.isNumeric(dto.getSerialNumber())) {//数字类型
            historicProcessInstanceQuery.variableValueEquals(WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY, Long.valueOf(dto.getSerialNumber()));
        }
        if (!ObjectUtil.isNull(dto.getStartTime())) {
            historicProcessInstanceQuery.startedAfter(WorkFlowUtil.getStartOfDay(dto.getStartTime()));
        }
        if (!ObjectUtil.isNull(dto.getEndTime())) {
            historicProcessInstanceQuery.startedBefore(WorkFlowUtil.getEndOfDay(dto.getEndTime()));
        }
        if (StrUtil.isNotBlank(dto.getName())) {
            historicProcessInstanceQuery.variableValueLike(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY, StringPool.PERCENT + dto.getName() + StringPool.PERCENT);
        }
        if (StrUtil.isNotBlank(dto.getSchemaId())) {
            historicProcessInstanceQuery.variableValueEquals(WorkflowConstant.PROCESS_SCHEMA_ID_KEY, Convert.toLong(dto.getSchemaId()));
        }
        if (StrUtil.isNotBlank(dto.getKeyword())) {
            historicProcessInstanceQuery.variableValueLike(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY, StringPool.PERCENT + dto.getKeyword() + StringPool.PERCENT);
        }
        if (StrUtil.isNotBlank(dto.getOriginator())) {
            historicProcessInstanceQuery.or()
                    .variableValueEquals(WorkflowConstant.PROCESS_START_USER_NAME_KEY, dto.getOriginator())
                    .variableValueEquals(WorkflowConstant.PROCESS_START_USER_ID_KEY, dto.getOriginator())
                    .endOr();
        }

        if (!ObjectUtil.isNull(dto.getType()) && dto.getType() == 1) {
            historicProcessInstanceQuery.finished();
        }
        if (!ObjectUtil.isNull(dto.getType()) && dto.getType() == 2) {
            historicProcessInstanceQuery.unfinished();
        }


        long count = historicProcessInstanceQuery.count();
        List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery.orderByProcessInstanceStartTime().desc().listPage(Convert.toInt((dto.getLimit() - 1) * dto.getSize()), dto.getSize());

        //获取到所有流程id
        List<String> processIds = historicProcessInstances.stream().map(HistoricProcessInstance::getId).collect(Collectors.toList());


        List<MyProcessPageVo> result = new ArrayList<>(historicProcessInstances.size());
        if (processIds.size() > 0) {

            List<WorkflowExtra> workflowExtras = workflowExtraMapper.selectList(Wrappers.lambdaQuery(WorkflowExtra.class).in(WorkflowExtra::getProcessId, processIds));

            for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {

                //找到当前流程的 任务开始时间 最大值  为当前审批节点
                workflowExtras.stream()
                        .filter(e -> e.getProcessId().equals(historicProcessInstance.getId()))
                        .max(Comparator.comparing(WorkflowExtra::getStartTime))
                        .ifPresent(e -> {
                            MyProcessPageVo vo = new MyProcessPageVo();
                            vo.setId(e.getId());
                            vo.setSerialNumber(e.getSerialNumber());
                            vo.setSchemaName(e.getSchemaName());
                            vo.setOriginator(e.getStartUserName());
                            vo.setSchemaId(e.getSchemaId());

                            vo.setTaskName(e.getTaskName());
                            vo.setCurrentTaskName(e.getTaskName());
                            vo.setTaskId(e.getTaskId());
                            vo.setCreateTime(e.getStartTime());
                            vo.setProcessId(e.getProcessId());
                            vo.setProcessName(e.getProcessName());

                            //如果是已经完成的任务 默认是100进度
                            if (ObjectUtil.isNotNull(historicProcessInstance.getEndTime())) {
                                vo.setCurrentProgress(100);
                                vo.setCurrentTaskName(WorkflowConstant.END_NODE_TYPE_NAME);
                            } else {
                                vo.setCurrentProgress(e.getCurrentProgress());
                            }

                            result.add(vo);
                        });


                MyProcessPageVo vo = new MyProcessPageVo();

            }
        }

        if (StrUtil.isNotBlank(dto.getSerialNumber()) && !StrUtil.isNumeric(dto.getSerialNumber())) {//非数字类型置空
            result.clear();
        }
        PageOutput<MyProcessPageVo> output = new PageOutput<>();
        output.setCurrentPage(dto.getLimit());
        output.setPageSize(dto.getSize());
        output.setTotal(Convert.toInt(count));
        output.setList(result);
        return output;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean moveRecycle(MoveRecycleDto dto) {


//        User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
//
//        Object startUserId = runtimeService.getVariable(processInstanceId, WorkflowConstant.PROCESS_START_USER_ID_KEY);
//
//        //判断是否为发起人
//        if (!ObjectUtil.equals(startUserId, user.getId())) {
//            throw new MyException("您不是发起人 无法移入回收站!");
//        }

        //此代码为适配老版本功能  将移入回收站的流程挂起
        {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(dto.getProcessId()).singleResult();

            if (processInstance != null) {
                VariableMap variableMap = Variables.createVariables().putValue(WorkflowConstant.PROCESS_ISRECYCLE_FLAG_KEY, WorkflowIsRecycleType.YES.getCode());

                runtimeService.setVariables(dto.getProcessId(), variableMap);
//                runtimeService.deleteProcessInstance(dto.getProcessId(), "流程移入回收站");

            } else {
                HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(dto.getProcessId()).singleResult();
                if (historicProcessInstance.getEndTime() != null) {
                    throw new MyException("流程已结束不能放入回收站！");
                } else {
                    throw new MyException("找不到此流程！");
                }
            }


        }

        //此代码为直接关闭流程代码
        {
//        //获取到当前活动的实例
//        ActivityInstance activityInstance = runtimeService.getActivityInstance(processInstanceId);
//
//        //先停止当前活动示例  然后  关闭流程
//        runtimeService.createProcessInstanceModification(processInstanceId)
//                .cancelAllForActivity(activityInstance.getId())
//                .setAnnotation("【发起人:" + user.getName() + "】 将 【流程:" + processInstanceId + "】 移入回收站 所有流程关闭！" )
//                .execute();
        }

        return true;
    }

    @Override
    public PageOutput<RecycleProcessPageVo> getRecycleProcessPage(RecycleProcessPageDto dto) {

        if (ObjectUtil.isNotNull(dto.getStartTime()) && ObjectUtil.isNotNull(dto.getEndTime())) {
            dto.setStartTime(WorkFlowUtil.getStartOfDay(dto.getStartTime()));
            dto.setEndTime(WorkFlowUtil.getEndOfDay(dto.getEndTime()));
        }

        User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());

        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
                .variableValueEquals(WorkflowConstant.PROCESS_START_USER_ID_KEY, user.getId())
                .variableValueEquals(WorkflowConstant.PROCESS_ISRECYCLE_FLAG_KEY, WorkflowIsRecycleType.YES.getCode());

        if (StrUtil.isNotBlank(dto.getSerialNumber()) && StrUtil.isNumeric(dto.getSerialNumber())) {
            historicProcessInstanceQuery.variableValueEquals(WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY, Long.valueOf(dto.getSerialNumber()));
        }
        if (!ObjectUtil.isNull(dto.getStartTime())) {
            historicProcessInstanceQuery.startedAfter(dto.getStartTime());
        }
        if (!ObjectUtil.isNull(dto.getEndTime())) {
            historicProcessInstanceQuery.startedBefore(dto.getEndTime());
        }
        if (StrUtil.isNotBlank(dto.getName())) {
            historicProcessInstanceQuery.variableValueEquals(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY, StringPool.PERCENT + dto.getName() + StringPool.PERCENT);
        }
        if (StrUtil.isNotBlank(dto.getInitiator())) {
            historicProcessInstanceQuery.variableValueEquals(WorkflowConstant.PROCESS_START_USER_NAME_KEY, dto.getInitiator());
        }

        long count = historicProcessInstanceQuery.count();
        List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery.listPage(Convert.toInt((dto.getLimit() - 1) * dto.getSize()), dto.getSize());

        //获取到所有流程id
        List<String> processIds = historicProcessInstances.stream().map(HistoricProcessInstance::getId).collect(Collectors.toList());


        List<RecycleProcessPageVo> result = new ArrayList<>(historicProcessInstances.size());
        if (processIds.size() > 0) {

            List<WorkflowExtra> workflowExtras = workflowExtraMapper.selectList(Wrappers.lambdaQuery(WorkflowExtra.class).in(WorkflowExtra::getProcessId, processIds));

            for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {

                //找到当前流程的 任务开始时间 最大值  为当前审批节点
                workflowExtras.stream()
                        .filter(e -> e.getProcessId().equals(historicProcessInstance.getId()))
                        .max(Comparator.comparing(WorkflowExtra::getStartTime))
                        .ifPresent(e -> {
                            RecycleProcessPageVo vo = new RecycleProcessPageVo();
                            vo.setSerialNumber(e.getSerialNumber());
                            vo.setSchemaName(e.getSchemaName());
                            vo.setOriginator(e.getStartUserName());
                            vo.setSchemaId(e.getSchemaId());
                            vo.setCurrentProgress(e.getCurrentProgress());
                            vo.setTaskName(e.getTaskName());
                            vo.setProcessId(e.getProcessId());
                            vo.setCreateTime(e.getLaunchTime());
                            vo.setCurrentTaskName(e.getTaskName());
                            vo.setTaskId(e.getTaskId());
                            vo.setProcessName(e.getProcessName());
                            result.add(vo);
                        });
            }
        }
        if (StrUtil.isNotBlank(dto.getSerialNumber()) && !StrUtil.isNumeric(dto.getSerialNumber())) {
            result.clear();
        }

        PageOutput<RecycleProcessPageVo> output = new PageOutput<>();
        output.setCurrentPage(dto.getLimit());
        output.setPageSize(dto.getSize());
        output.setTotal(Convert.toInt(count));
        result.sort((t1, t2) -> t2.getCreateTime().compareTo(t1.getCreateTime()));
        output.setList(result);
        return output;
    }

    @Override
    public RestartVo restart(RestartDto dto) {

        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, dto.getSchemaId()).select(x -> !x.getProperty().startsWith(DB_FIELD_XML_PREFIX)));

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);

        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(dto.getProcessId()).singleResult();

        Optional<Map<String, Object>> startNodeMapOp = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(historicProcessInstance.getStartActivityId())).findFirst();

        if (!startNodeMapOp.isPresent()) {
            throw new MyException("找不到开始节点的配置信息");
        }

        //将map 转为 java类
        StartNodeConfig startNodeConfig = Convert.convert(StartNodeConfig.class, startNodeMapOp.get());

        RestartVo vo = new RestartVo();
        vo.setFormDatas(new HashMap<>());

        String[] startNodeFormKey = startNodeConfig.getFormConfigs().stream().map(c -> WorkflowConstant.PROCESS_FORMDATA_PREFIX_KEY + c.getFormId()).toArray(String[]::new);

        List<HistoricVariableInstance> list = historyService.createHistoricVariableInstanceQuery().processInstanceId(dto.getProcessId()).variableNameIn(startNodeFormKey).list();

        for (HistoricVariableInstance historicVariableInstance : list) {
            vo.getFormDatas().put(historicVariableInstance.getName(), Convert.toMap(String.class, Object.class, historicVariableInstance.getValue()));
        }


        vo.setSchemaInfo(BeanUtil.toBean(workflowSchema, WorkflowSchemaInfoVo.class));
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean recycleDelete(RecycleDeleteDto dto) {

        for (String processId : dto.getProcessIds()) {
            runtimeService.deleteProcessInstance(processId, "流程移入回收站");
        }
        return true;
    }

    @Override
    public List<HistoryTaskVo> getHistoryTask(String schemaId, String processInstanceId) {

        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).list();
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, schemaId));
//        WorkflowSchema workflowSchema = redisUtil.get(WorkflowConstant.SCHEMA_CACHE_PREFIX + schemaId, WorkflowSchema.class);

        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);

        List<HistoryTaskVo> result = new ArrayList<>();

        for (HistoricTaskInstance task : list) {
            Optional<Map<String, Object>> taskConfigMapOp = workflowSchemaConfig.getChildNodeConfig()
                    .stream()
                    .filter(n -> n.containsValue(task.getTaskDefinitionKey()))
                    .findFirst();

            taskConfigMapOp.ifPresent(map -> {
                //将map 转为 java类
                UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, map);
                HistoryTaskVo vo = new HistoryTaskVo();
                //必须非会签系节点
                if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() == WorkflowMultiInstanceType.NONE.getCode()) {
                    vo.setTaskId(task.getId());
                    vo.setTaskName(task.getName());
                    result.add(vo);
                }

            });
        }

        return result;
    }

    @Override
    public boolean withdraw(WithdrawDto dto) {

        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(dto.getProcessId()).singleResult();

        User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());

        if (historicProcessInstance.getEndTime() != null) {
            throw new MyException("流程已经结束  无法撤回！");
        }
        //如果是简单流程 无多实例 无网关等组件 默认可以根据审批时间

        //获取到当前活动的实例
        ActivityInstance activityInstance = runtimeService.getActivityInstance(dto.getProcessId());

        //获取当前活动的任务信息
        List<WorkflowExtra> workflowExtras = workflowExtraMapper.selectList(Wrappers.lambdaQuery(WorkflowExtra.class).in(WorkflowExtra::getProcessId, dto.getProcessId()));
        //找到当前流程的 任务开始时间 最大值  为当前审批节点
        WorkflowExtra workflowExtra = workflowExtras.stream()
                .filter(e -> e.getProcessId().equals(dto.getProcessId()))
                .max(Comparator.comparing(WorkflowExtra::getStartTime)).orElse(new WorkflowExtra());
        //新增流程发起流程记录
        WorkflowRecord record = new WorkflowRecord();
        record.setNodeId(workflowExtra.getTaskId());
        record.setNodeName(workflowExtra.getTaskName());
        record.setNodeType(workflowExtra.getTaskName());
        record.setProcessId(dto.getProcessId());
        record.setSchemaId(workflowExtra.getSchemaId());
        record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
        record.setRecordTime(LocalDateTime.now());
        String oldTaskName = workflowExtra.getTaskName();
        //如果传入的是开始节点的值  默认是关闭流程  重新发起
        if (StrUtil.equals(dto.getActivityId(), historicProcessInstance.getStartActivityId())) {

            runtimeService.deleteProcessInstance(historicProcessInstance.getId(), "【发起人:" + user.getName() + "】 将 【流程:" + historicProcessInstance.getProcessDefinitionName() + "】 撤回 到  开始节点！");
//            //先停止当前活动示例  然后  将流程撤回到开始节点
//            runtimeService.createProcessInstanceModification(dto.getProcessId())
//                    .cancelActivityInstance(activityInstance.getId())
//                    .setAnnotation("【发起人:" + user.getName() + "】 将 【流程:" + historicProcessInstance.getProcessDefinitionName() + "】 撤回 到  开始节点！")
//                    .startBeforeActivity(historicProcessInstance.getStartActivityId())
//                    .setVariableLocal(WorkflowConstant.TASK_IS_APPOINT_APPROVE,YesOrNoEnum.NO.getCode())
//                    .execute();
            record.setMessage("【" + user.getName() + "】 将当前流程从【" + oldTaskName + "】 撤回 到 【开始节点】！");
        } else {
            //先停止当前活动示例  然后  将流程撤回到某个节点
            runtimeService.createProcessInstanceModification(historicProcessInstance.getId())
                    .cancelActivityInstance(activityInstance.getId())
                    .setAnnotation("【发起人:" + user.getName() + "】 将 【流程:" + historicProcessInstance.getProcessDefinitionName() + "】 撤回 到 【任务：" + activityInstance.getActivityName() + "】！")
                    .startBeforeActivity(dto.getActivityId())
                    .setVariableLocal(WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode())
                    .execute();

            //获取当前活动的任务信息
            List<WorkflowExtra> workflowExtrasNew = workflowExtraMapper.selectList(Wrappers.lambdaQuery(WorkflowExtra.class).in(WorkflowExtra::getProcessId, dto.getProcessId()));
            //找到当前流程的 任务开始时间 最大值  为当前审批节点
            WorkflowExtra workflowExtraNew = workflowExtrasNew.stream()
                    .filter(e -> e.getProcessId().equals(dto.getProcessId()))
                    .max(Comparator.comparing(WorkflowExtra::getStartTime)).orElse(new WorkflowExtra());
            record.setMessage("【" + user.getName() + "】 将当前流程从【" + oldTaskName + "】撤回 到【" + workflowExtraNew.getTaskName() + "】！");
        }
        //保存流程撤回信息
        workflowRecordMapper.insert(record);
        return true;
    }

    @Override
    public boolean saveDraft(SaveDraftDto dto) {

        WorkflowDraft draft = BeanUtil.toBean(dto, WorkflowDraft.class);
        draft.setFormData(JSONUtil.toJsonStr(dto.getFormData()));

        return workflowDraftService.save(draft);
    }

    @Override
    public boolean updateDraft(UpdateDraftDto dto) {

        WorkflowDraft draft = new WorkflowDraft();
        draft.setId(dto.getId());
        draft.setFormData(JSONUtil.toJsonStr(dto.getFormData()));

        return workflowDraftService.updateById(draft);

    }

    @Override
    public PageOutput<DraftPageVo> draftPage(DraftPageDto dto) {

        if (ObjectUtil.isNotNull(dto.getStartTime()) && ObjectUtil.isNotNull(dto.getEndTime())) {
            dto.setStartTime(WorkFlowUtil.getStartOfDay(dto.getStartTime()));
            dto.setEndTime(WorkFlowUtil.getEndOfDay(dto.getEndTime()));
        }

        List<Long> ids = new ArrayList<>();
        //发起人分割数组
        if (StrUtil.isNotBlank(dto.getOriginator())) {
            String allUserIdStr = StrUtil.join(StringPool.COMMA, dto.getOriginator());
            ids = Arrays.stream(allUserIdStr.split(StringPool.COMMA)).map(Convert::toLong).collect(Collectors.toList());
        }
        ids.add(StpUtil.getLoginIdAsLong());

        //因为多表关联 会有多个表都使用了id字段，  所以必须专门指定主表的Id
        IPage<DraftPageVo> page = workflowDraftService.selectJoinListPage(ConventPage.getPage(dto), DraftPageVo.class,
                MPJWrappers.<WorkflowDraft>lambdaJoin()
                        .disableSubLogicDel()
                        .eq(StrUtil.isNotBlank(dto.getName()), WorkflowSchema::getName, dto.getName())
                        .in(CollectionUtil.isNotEmpty(ids), WorkflowDraft::getCreateUserId, ids)
                        .between(ObjectUtil.isNotNull(dto.getStartTime()) && ObjectUtil.isNotNull(dto.getEndTime()), WorkflowDraft::getCreateDate, dto.getStartTime(), dto.getEndTime())
                        .orderByDesc(WorkflowDraft::getCreateDate)
                        .select(WorkflowDraft::getId)
                        .select(WorkflowDraft.class, x -> VoToColumnUtil.fieldsToColumns(DraftPageVo.class).contains(x.getProperty()))
                        .selectAs(WorkflowSchema::getName, DraftPageVo::getSchemaName)
                        .selectAs(User::getName, DraftPageVo::getOriginator)
                        .leftJoin(WorkflowSchema.class, WorkflowSchema::getId, WorkflowDraft::getSchemaId)
                        .leftJoin(User.class, User::getId, WorkflowDraft::getCreateUserId));

        return ConventPage.getPageOutput(page);

    }

    @Override
    public boolean deleteDraft(List<Long> ids) {
        return workflowDraftService.removeBatchByIds(ids);
    }

    @Override
    public DraftInfoVo draftData(Long id) {
        WorkflowDraft draft = workflowDraftService.getById(id);
        return BeanUtil.toBean(draft, DraftInfoVo.class);
    }

    @Override
    public PageOutput<MonitorPageVo> getProcessMonitorPage(MonitorPageDto dto) {

        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();

        if (!ObjectUtil.isNull(dto.getStartTime())) {
            historicProcessInstanceQuery.startedAfter(WorkFlowUtil.getStartOfDay(dto.getStartTime()));
        }
        if (!ObjectUtil.isNull(dto.getEndTime())) {
            historicProcessInstanceQuery.startedBefore(WorkFlowUtil.getEndOfDay(dto.getEndTime()));
        }
        if (StrUtil.isNotBlank(dto.getKeyword())) {
            historicProcessInstanceQuery
                    .or()
                    .variableValueLike(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY, StringPool.PERCENT + dto.getKeyword() + StringPool.PERCENT)
                    .variableValueLike(WorkflowConstant.PROCESS_START_USER_NAME_KEY, StringPool.PERCENT + dto.getKeyword() + StringPool.PERCENT)
                    .variableValueEquals(WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY, StrUtil.isNumeric(dto.getKeyword()) ? Convert.toLong(dto.getKeyword()) : 0L)
                    .endOr();
        }
        if (dto.getType() == 1) {
            historicProcessInstanceQuery.finished();
        }
        if (dto.getType() == 0) {
            historicProcessInstanceQuery.unfinished();
        }

        long count = historicProcessInstanceQuery.count();
//        List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery.listPage(Convert.toInt((dto.getLimit() - 1) * dto.getSize()), dto.getSize());
        List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery.list();

        //获取到所有流程id
        List<String> processIds = historicProcessInstances.stream().map(HistoricProcessInstance::getId).collect(Collectors.toList());


        List<MonitorPageVo> result = new ArrayList<>(historicProcessInstances.size());
        if (processIds.size() > 0) {

            List<WorkflowExtra> workflowExtras = workflowExtraMapper.selectList(Wrappers.lambdaQuery(WorkflowExtra.class).in(WorkflowExtra::getProcessId, processIds));

            for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {


                //找到当前流程的 任务开始时间 最大值  为当前审批节点
                workflowExtras.stream()
                        .filter(e -> e.getProcessId().equals(historicProcessInstance.getId()))
                        .max(Comparator.comparing(WorkflowExtra::getStartTime))
                        .ifPresent(e -> {
                            MonitorPageVo vo = new MonitorPageVo();
                            vo.setProcessId(e.getProcessId());
                            vo.setTaskId(e.getTaskId());
                            vo.setSerialNumber(e.getSerialNumber());
                            vo.setSchemaName(e.getSchemaName());
                            vo.setOriginator(e.getStartUserName());
                            vo.setSchemaId(e.getSchemaId());

                            //如果是已经完成的任务 默认是100进度
                            if (ObjectUtil.isNotNull(historicProcessInstance.getEndTime())) {
                                vo.setCurrentProgress(100);
                            } else {
                                vo.setCurrentProgress(e.getCurrentProgress());
                            }
                            vo.setCurrentTaskName(e.getTaskName());

                            //如果当前任务是外部流程任务
                            if (StrUtil.isBlank(e.getTaskId())) {//只有可能是外部流程任务或者脚本任务，但脚本任务会直接完成度100%，所以直接设值为外部流程任务
                                vo.setCurrentTaskName(WorkflowConstant.CALL_ACTIVITY_TYPE_NAME);
                            }
                            vo.setStatus(historicProcessInstance.getState());
                            vo.setStatusMessage(historicProcessInstance.getState().equals("SUSPENDED") ? "挂起" : "");
                            vo.setCreateDate(e.getStartTime());

                            result.add(vo);
                        });
            }
        }
        result.sort((t1, t2) -> t2.getCreateDate().compareTo(t1.getCreateDate()));
        PageOutput<MonitorPageVo> output = new PageOutput<>();
        output.setCurrentPage(dto.getLimit());
        output.setPageSize(dto.getSize());
        output.setTotal(Convert.toInt(count));
        output.setList(result);
        return output;
    }

    @Override
    public boolean setAssignee(SetAssigneeDto dto) {
        Task task = taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();
        if (task == null) {
            throw new MyException("找不到此流程！");
        }
        if (task.isSuspended()) {
            throw new MyException("流程已挂起，无法指定审批人！");
        }

        Object assigneesObject = taskService.getVariable(dto.getTaskId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY);

        List<String> assignees = ListUtil.toList(Convert.toStr(assigneesObject).split(StringPool.COMMA));
        assignees.addAll(dto.getAssignees());
        taskService.setVariable(dto.getTaskId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StrUtil.join(StringPool.COMMA, assignees));

        Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
        //获取当前用户的信息
        User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
        //新增流程发起流程记录
        WorkflowRecord record = new WorkflowRecord();
        record.setNodeId(task.getId());
        record.setNodeName(task.getName());
        record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
        record.setProcessId(task.getProcessInstanceId());
        record.setSchemaId(schemaId);
        record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
        record.setRecordTime(LocalDateTime.now());
        //[操作人名称] 通过[指派审核人] 指派[被指派人名称]作为审核人
        List<Long> allAssigneesUserIds = dto.getAssignees().stream().mapToLong(t -> Long.parseLong(t.trim())).boxed().collect(Collectors.toList());
        String allAssigneesUserNames = getAllUserNamesByIds(allAssigneesUserIds);
        record.setMessage("【" + user.getName() + "】通过【指派审核人】指派【" + allAssigneesUserNames + "】作为审核人");

        workflowRecordMapper.insert(record);
        return true;
    }

    @Override
    public boolean setSuspended(SetSuspendedDto dto) {

        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(dto.getProcessId()).singleResult();

        List<Task> taskList = taskService.createTaskQuery()
                .processInstanceId(dto.getProcessId())
                .list();
        Task task = taskList.stream().filter(x -> x.getProcessInstanceId().equals(dto.getProcessId())).findFirst().orElse(new TaskEntity());
        //获取当前用户的信息
        User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
        Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
        //新增流程发起流程记录
        WorkflowRecord record = new WorkflowRecord();
        record.setNodeId(task.getId());
        record.setNodeName(task.getName());
        record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
        record.setProcessId(task.getProcessInstanceId());
        record.setSchemaId(schemaId);
        record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
        record.setRecordTime(LocalDateTime.now());

        if (processInstance.isSuspended()) {
            runtimeService.activateProcessInstanceById(dto.getProcessId());
            //[操作人名称] 将流程恢复
            record.setMessage("【" + user.getName() + "】将流程恢复");
            workflowRecordMapper.insert(record);
        } else {
            runtimeService.suspendProcessInstanceById(dto.getProcessId());
            //[操作人名称] 将流程挂起
            record.setMessage("【" + user.getName() + "】将流程挂起");
            workflowRecordMapper.insert(record);
        }

        return true;
    }

    @Override
    public List<GetAssigneeVo> getAssignee(GetAssigneeDto dto) {
        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, dto.getSchemaId()).select(x -> !x.getProperty().startsWith(DB_FIELD_XML_PREFIX)));

        if (workflowSchema == null) {
            throw new MyException("找不到此流程模板！");
        }

        //如果当前模板被禁用 无法操作流程
        if (workflowSchema.getEnabledMark() == EnabledMark.DISABLED.getCode()) {
            throw new MyException("流程被禁用！");
        }

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);

        Task task = taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();

        Optional<Map<String, Object>> userTaskConfigOp = workflowSchemaConfig.getChildNodeConfig().stream().filter(c -> c.containsValue(task.getTaskDefinitionKey())).findFirst();

        if (!userTaskConfigOp.isPresent()) {
            throw new MyException("找不到当前节点配置！");
        }
        //    将map 转为 java类
        UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskConfigOp.get());

        List<User> users = redisUtil.get(GlobalConstant.USER_CACHE_KEY, new TypeReference<List<User>>() {
        });
        List<GetAssigneeVo> result = new ArrayList<>();

        //如果是单实例任务
        if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() == WorkflowMultiInstanceType.NONE.getCode()) {
            Object assigneeObj = taskService.getVariable(dto.getTaskId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY);
            List<String> taskAssignee = StrUtil.split(Convert.toStr(assigneeObj), StringPool.COMMA);


            List<User> assigneeUserInfo = users.stream().filter(u -> taskAssignee.contains(StrUtil.toString(u.getId()))).collect(Collectors.toList());

            for (User user : assigneeUserInfo) {
                GetAssigneeVo vo = BeanUtil.toBean(user, GetAssigneeVo.class);

                //如果不是当前登录用户 就可以移除
                vo.setCanRemove(StpUtil.getLoginIdAsLong() != user.getId());
                result.add(vo);
            }

        }
        //如果是多实例任务
        else {

            Object assigneeObj = runtimeService.getVariable(task.getProcessInstanceId(), WorkflowConstant.TASK_MULTI_ASSIGNEE_VAR_KEY + task.getTaskDefinitionKey());
            List<Long> taskAssignee = Convert.toList(Long.class, assigneeObj);

            List<User> assigneeUserInfo = users.stream().filter(u -> taskAssignee.contains(u.getId())).collect(Collectors.toList());

            //查询审批记录表，xjr_workflow_approve_record，流程id和节点的key值查询有哪些用户审批过了
            List<WorkflowApproveRecord> list = approveRecordService.list(Wrappers.lambdaQuery(WorkflowApproveRecord.class)
                    .eq(WorkflowApproveRecord::getProcessId, task.getProcessInstanceId())
                    .eq(WorkflowApproveRecord::getTaskDefinitionKey, task.getTaskDefinitionKey())
            );

            List<Long> approveUserIds = new ArrayList<>();
            if (list.size() != 0) {
                //已审批的用户id
                approveUserIds = list.stream().map(WorkflowApproveRecord::getApproveUserId).collect(Collectors.toList());
            }
            for (User user : assigneeUserInfo) {
                GetAssigneeVo vo = BeanUtil.toBean(user, GetAssigneeVo.class);

                //同步都不能减签
                if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() == WorkflowMultiInstanceType.PARALLEL.getCode()) {
                    vo.setCanRemove(Boolean.FALSE);
                } else {
                    //异步不能减签已经审批的和当前审批登录人
                    //如果人员已经审批了  或者  审批人是当前登陆人 不能移除
                    if (approveUserIds.contains(user.getId()) || StpUtil.getLoginIdAsLong() == user.getId()) {
                        vo.setCanRemove(Boolean.FALSE);
                    } else {
                        vo.setCanRemove(Boolean.TRUE);
                    }
                }

                result.add(vo);
            }


        }
        return result;
    }

    @Override
    public boolean addOrSubSign(AddOrSubSignDto dto) {
        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, dto.getSchemaId()).select(x -> !x.getProperty().startsWith(DB_FIELD_XML_PREFIX)));

        if (workflowSchema == null) {
            throw new MyException("找不到此流程模板！");
        }
        //如果当前模板被禁用 无法操作流程
        if (workflowSchema.getEnabledMark() == EnabledMark.DISABLED.getCode()) {
            throw new MyException("流程被禁用！");
        }

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);

        Task task = taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();

        Optional<Map<String, Object>> userTaskConfigOp = workflowSchemaConfig.getChildNodeConfig().stream().filter(c -> c.containsValue(task.getTaskDefinitionKey())).findFirst();

        if (!userTaskConfigOp.isPresent()) {
            throw new MyException("找不到当前节点配置！");
        }
        //    将map 转为 java类
        UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskConfigOp.get());

        if (!userTaskConfig.getCountersignConfig().getAddOrRemove()) {
            throw new MyException("当前节点不允许加减签！");
        }

        //如果是单实例任务
        if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() == WorkflowMultiInstanceType.NONE.getCode()) {
            Object assigneeObj = taskService.getVariable(dto.getTaskId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY);
            List<String> taskAssigneeStrList = StrUtil.split(Convert.toStr(assigneeObj), StringPool.COMMA);

            List<Long> assigneeList = Convert.toList(Long.class, taskAssigneeStrList);
            assigneeList.removeAll(dto.getUserIds());

            taskService.setVariableLocal(dto.getTaskId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StrUtil.join(StringPool.COMMA, assigneeList));
        } else {

            Object assigneeObj = runtimeService.getVariable(task.getProcessInstanceId(), WorkflowConstant.TASK_MULTI_ASSIGNEE_VAR_KEY + task.getTaskDefinitionKey());
            List<Long> taskAssignee = Convert.toList(Long.class, assigneeObj);

            //取交集 不用动
            Collection<Long> intersection = CollectionUtil.intersection(taskAssignee, dto.getUserIds());

            //使用交集 与 原审批人 取差集  就是需要减签的成员
            List<Long> removeUser = CollectionUtil.subtractToList(taskAssignee, intersection);
            //使用交集 与 新审批人 取差集  就是需要加签的成员
            List<Long> addUser = CollectionUtil.subtractToList(dto.getUserIds(), intersection);

            int instancesCount = 0;
            Object allInstances = runtimeService.getVariable(task.getExecutionId(), WorkflowConstant.MULTI_INSTANCE_VAR_KEY);


            //如果是串行（异步）
            if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() == WorkflowMultiInstanceType.SEQUENTIAL.getCode()) {
                //减签
                taskAssignee.removeAll(removeUser);
                instancesCount = Convert.toInt(allInstances) - removeUser.size();

                //加签
                taskAssignee.addAll(addUser);
                instancesCount += addUser.size();
                runtimeService.setVariable(task.getProcessInstanceId(), WorkflowConstant.TASK_MULTI_ASSIGNEE_VAR_KEY + task.getTaskDefinitionKey(), taskAssignee);

            }
            //同步需要直接开一个任务
            else {
                //减签
                for (Long userId : removeUser) {
                    Task removeTask = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).taskVariableValueEquals(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StrUtil.toString(userId)).singleResult();

                    //获取到 activityinstanceId
                    HistoricActivityInstance historicActivityInstance = historyService.createHistoricActivityInstanceQuery()
                            .processInstanceId(removeTask.getProcessInstanceId())
                            .activityId(task.getTaskDefinitionKey())
                            .executionId(removeTask.getExecutionId()).singleResult();

                    runtimeService.createProcessInstanceModification(task.getProcessInstanceId())
                            .cancelActivityInstance(historicActivityInstance.getId())
                            .execute();
                }
                instancesCount = Convert.toInt(allInstances) - removeUser.size();
                taskAssignee.removeAll(removeUser);

                //加签
                for (Long userId : addUser) {
                    runtimeService.createProcessInstanceModification(task.getProcessInstanceId())
                            .startBeforeActivity(task.getTaskDefinitionKey())
                            .setVariableLocal(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, userId)
                            .execute();
                }
                //加签
                taskAssignee.addAll(addUser);
                instancesCount += addUser.size();
                runtimeService.setVariable(task.getProcessInstanceId(), WorkflowConstant.TASK_MULTI_ASSIGNEE_VAR_KEY + task.getTaskDefinitionKey(), taskAssignee);
            }
            runtimeService.setVariable(task.getExecutionId(), WorkflowConstant.MULTI_INSTANCE_VAR_KEY, instancesCount);

        }
        return true;
    }

    @Override
    public List<RejectNodeVo> getRejectNode(RejectNodeDto dto) {

        ActivityInstance activityInstance = runtimeService.getActivityInstance(dto.getProcessId());
        if (activityInstance == null) {
            throw new MyException("流程已经结束，不允许撤回！");
        }

        HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(dto.getProcessId()).variableName(WorkflowConstant.PROCESS_SCHEMA_ID_KEY).singleResult();

        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, historicVariableInstance.getValue()));

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);

        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(dto.getProcessId())
                .activityType(WorkflowConstant.BPMN_USER_TASK_TYPE_NAME)
                .finished()
                .orderByHistoricActivityInstanceEndTime()
                .desc()
                .list();

        String taskDefinitionKey;
        if (ObjectUtil.isNotNull(dto.getTaskId())) {
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(dto.getTaskId()).singleResult();
            taskDefinitionKey = historicTaskInstance.getTaskDefinitionKey();
        } else {
            //一个用户任务也不存在的情况。只有可能是纯粹的脚本任务和开始节点后面直接接外部流程的情况，直接不允许撤回
            if (list.size() == YesOrNoEnum.NO.getCode()) {
                throw new MyException("当前流程不允许撤回 ");
            } else {//正在进行外部流程的子流程运行中
                throw new MyException("当前流程有其他外部流程 或者 子流程 正在运行，不允许撤回！");
            }
//            taskDefinitionKey = activityInstance.getChildActivityInstances()[0].getActivityId();
        }

//        Task task = taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();

        Optional<Map<String, Object>> userTaskConfigOp = workflowSchemaConfig.getChildNodeConfig().stream().filter(c -> c.containsValue(taskDefinitionKey)).findFirst();

        if (!userTaskConfigOp.isPresent()) {
            throw new MyException("找不到当前节点配置！");
        }
        //    将map 转为 java类
        UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskConfigOp.get());

        List<RejectNodeVo> voList = new ArrayList<>();

        List<HistoricProcessInstance> subProcess = historyService.createHistoricProcessInstanceQuery()
                .superProcessInstanceId(dto.getProcessId()).list();

        //当前流程是否有父级流程,就是外部流程或者子流程。外部流程或者子流程不能撤回到开始节点，不新增开始节点
        if (subProcess.size() < 1) {
            //获取到当前流程发起人  如果操作人 是发起人 默认新增开始节点
            Object startUserId = runtimeService.getVariable(dto.getProcessId(), WorkflowConstant.PROCESS_START_USER_ID_KEY);
            if (StpUtil.getLoginIdAsLong() == Convert.toLong(startUserId)) {

                List<HistoricActivityInstance> startList = historyService.createHistoricActivityInstanceQuery()
                        .processInstanceId(dto.getProcessId())
                        .activityType(WorkflowConstant.BPMN_START_EVENT_TYPE_NAME)
                        .finished()
                        .orderByHistoricActivityInstanceEndTime()
                        .desc()
                        .list();

                HistoricActivityInstance historicActivityInstance = startList.get(0);

                RejectNodeVo vo = new RejectNodeVo();
                vo.setActivityId(historicActivityInstance.getActivityId());
                vo.setActivityName(historicActivityInstance.getActivityName());
                voList.add(vo);
            }
        }

        if (list == null || list.size() == 0) {
            return voList;
        }

        List<ButtonConfig> buttonConfigs = userTaskConfig.getButtonConfigs();

        if (CollectionUtil.isNotEmpty(buttonConfigs)) {
            Optional<ButtonConfig> rejectButton = buttonConfigs.stream().filter(ButtonConfig::getChecked).filter(x -> x.getApproveType() == WorkflowApproveType.REJECT.getCode()).filter(x -> x.getButtonOpera().equals(YesOrNoEnum.YES.getCode())).findFirst();

            //如果有配置驳回按钮 并且 设置只能驳回上一节点
            if (rejectButton.isPresent()) {
                HistoricActivityInstance historicActivityInstance = list.get(0);

                if (!historicActivityInstance.isCanceled() && !activityInstance.getChildActivityInstances()[0].getActivityId().equals(historicActivityInstance.getActivityId())) {
                    RejectNodeVo vo = new RejectNodeVo();
                    vo.setActivityId(historicActivityInstance.getActivityId());
                    vo.setActivityName(historicActivityInstance.getActivityName());
                    voList.add(vo);
                }
                return voList;
            }
        }


        for (HistoricActivityInstance historicActivityInstance : list) {

            if (!historicActivityInstance.isCanceled() && !activityInstance.getChildActivityInstances()[0].getActivityId().equals(historicActivityInstance.getActivityId())) {
                RejectNodeVo vo = new RejectNodeVo();
                vo.setActivityId(historicActivityInstance.getActivityId());
                vo.setActivityName(historicActivityInstance.getActivityName());
                voList.add(vo);
            }

        }
        //多次驳回之后需要对返回的节点进行去重操作
        List<RejectNodeVo> collect = voList.stream().distinct().collect(Collectors.toList());
        return collect;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean transfer(TransferDto dto) {
        Object assigneeObj = taskService.getVariableLocal(dto.getTaskId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY);

        String newAssignee = StrUtil.toString(assigneeObj).replace(StpUtil.getLoginIdAsString(), dto.getUserId());

        taskService.setVariableLocal(dto.getTaskId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, newAssignee);

        //根据taskid  获取任务信息
        Task task = taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();
        Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
        //获取当前用户的信息
        User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
        //根据转办人的id获取用户名称
        List<User> userList = redisUtil.get(GlobalConstant.USER_CACHE_KEY, new TypeReference<List<User>>() {
        });
        List<String> collect = userList.stream().filter(u -> dto.getUserId().equals(Convert.toStr(u.getId()))).map(User::getName).collect(Collectors.toList());
        //转办人名称
        String newAssigneeName = collect.get(0);
        //新增流程发起流程记录
        WorkflowRecord record = new WorkflowRecord();
        record.setNodeId(task.getId());
        record.setNodeName(task.getName());
        record.setNodeType(WorkflowConstant.START_EVENT_TYPE_NAME);
        record.setProcessId(task.getProcessInstanceId());
        record.setSchemaId(schemaId);
        record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
        record.setRecordTime(LocalDateTime.now());
        //[操作人名称]将[节点名称] 转交给[被作人名称]办理
        String message = "【" + user.getName() + "】将【" + task.getName() + "】转交给【" + newAssigneeName + "】办理";
        record.setMessage(message);

        workflowRecordMapper.insert(record);
        return true;
    }

    @Override
    public GetCountVo getCount() {
        GetCountVo vo = new GetCountVo();
//        SaSession tokenSession = StpUtil.getTokenSession();
//        User user = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());

        TaskQuery taskQuery = taskService.createTaskQuery()
                .active()
                .taskVariableValueEquals(WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode())
                .processVariableValueEquals(WorkflowConstant.PROCESS_ISRECYCLE_FLAG_KEY, WorkflowIsRecycleType.NO.getCode())
                .taskVariableValueLike(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StringPool.PERCENT + StpUtil.getLoginIdAsLong() + StringPool.PERCENT);

        vo.setPendingCount(taskQuery.count());


        LambdaQueryWrapper<WorkflowDelegate> delegateQueryWrapper = Wrappers.lambdaQuery(WorkflowDelegate.class).like(WorkflowDelegate::getDelegateUserIds, StpUtil.getLoginIdAsLong());

        vo.setDelegateCount(workflowDelegateMapper.selectCount(delegateQueryWrapper));


        LambdaQueryWrapper<WorkflowApproveRecord> recordQueryWrapper = Wrappers.lambdaQuery(WorkflowApproveRecord.class).eq(WorkflowApproveRecord::getApproveUserId, StpUtil.getLoginIdAsLong());
        vo.setFinishedCount(approveRecordService.count(recordQueryWrapper));


        return vo;
    }


    /**
     * 根据流程id  获取流程流转信息
     *
     * @param processInstanceId
     * @return
     */
    private List<ProcessRecordListVo> getProcessRecordListVos(String processInstanceId, int onlySelf) {

        User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
        List<Long> roleIds = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_ROLE_ID_KEY, new ArrayList<>());
        LambdaQueryWrapper<WorkflowRecord> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (onlySelf == YesOrNoEnum.YES.getCode()) {//仅查看本人
            if (roleIds.contains(GlobalConstant.SUPER_ADMIN_ROLE_ID)) {//管理员数据
                lambdaQueryWrapper.eq(WorkflowRecord::getProcessId, processInstanceId)
                        .orderByAsc(WorkflowRecord::getRecordTime)
                        .like(WorkflowRecord::getMessage, user.getName())
                        .or()
                        .like(WorkflowRecord::getMessage, "流程结束")
                        .eq(WorkflowRecord::getProcessId, processInstanceId)
                        .or()
                        .like(WorkflowRecord::getMessage, "脚本节点")
                        .eq(WorkflowRecord::getCreateUserId, StpUtil.getLoginIdAsLong())//脚本任务
                        .eq(WorkflowRecord::getProcessId, processInstanceId);
            } else {//其他人数据
                lambdaQueryWrapper.eq(WorkflowRecord::getProcessId, processInstanceId)
                        .orderByAsc(WorkflowRecord::getRecordTime)
                        .eq(WorkflowRecord::getCreateUserId, StpUtil.getLoginIdAsLong())
                        .like(WorkflowRecord::getMessage, user.getName())
                        .or()
                        .like(WorkflowRecord::getMessage, user.getName())
                        .eq(WorkflowRecord::getProcessId, processInstanceId)
                        .or()
                        .like(WorkflowRecord::getMessage, "脚本节点")
                        .eq(WorkflowRecord::getCreateUserId, StpUtil.getLoginIdAsLong())//脚本任务
                        .eq(WorkflowRecord::getProcessId, processInstanceId);
            }
        } else {
            lambdaQueryWrapper.eq(WorkflowRecord::getProcessId, processInstanceId)
                    .orderByAsc(WorkflowRecord::getRecordTime);
        }

        List<WorkflowRecord> workflowRecords = workflowRecordMapper.selectList(lambdaQueryWrapper);

        List<ProcessRecordListVo> recordListVos = new ArrayList<>(workflowRecords.size());
        for (WorkflowRecord workflowRecord : workflowRecords) {
            ProcessRecordListVo recordListVo = new ProcessRecordListVo();
            recordListVo.setNodeType(workflowRecord.getNodeType());
            recordListVo.setNodeName(workflowRecord.getNodeName());
            recordListVo.setComment(workflowRecord.getMessage());
            recordListVo.setStartTime(workflowRecord.getRecordTime());
            recordListVo.setCirculateMessage(workflowRecord.getCirculateMessage());
            recordListVos.add(recordListVo);
        }
        return recordListVos;
    }


    private void invokeAutoAgree(String processInstanceId, Long schemaId, WorkflowSchemaConfig workflowSchemaConfig, List<Task> taskList) {

        if (taskList.size() == 0) {
        } else if (taskList.size() == 1) {
            Task task = taskList.get(0);
            Optional<Map<String, Object>> userTaskConfigMap = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(task.getTaskDefinitionKey())).findFirst();
            User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
            if (!userTaskConfigMap.isPresent()) {
                throw new MyException("当前任务没有开始节点");
            }
            //将map 转为 java类
            UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskConfigMap.get());
            List<Integer> userAutoAgree = userTaskConfig.getAutoAgreeRule();
            List<Integer> processAutoAgree = workflowSchemaConfig.getProcessConfig().getAutoAgreeRule();

            if (userAutoAgree != null && userAutoAgree.size() > 0) {
                isAutoAgree(processInstanceId, schemaId, workflowSchemaConfig, task, user, userAutoAgree);
                return;
            }
            if (processAutoAgree != null && processAutoAgree.size() > 0) {
                isAutoAgree(processInstanceId, schemaId, workflowSchemaConfig, task, user, processAutoAgree);
            }
        } else {
            for (Task task : taskList) {
                Optional<Map<String, Object>> userTaskConfigMap = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(task.getTaskDefinitionKey())).findFirst();
                User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
                if (!userTaskConfigMap.isPresent()) {
                    throw new MyException("当前任务没有开始节点");
                }
                //将map 转为 java类
                UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskConfigMap.get());
                List<Integer> userAutoAgree = userTaskConfig.getAutoAgreeRule();
                List<Integer> processAutoAgree = workflowSchemaConfig.getProcessConfig().getAutoAgreeRule();

                if (userAutoAgree != null && userAutoAgree.size() > 0) {
                    isAutoAgree(processInstanceId, schemaId, workflowSchemaConfig, task, user, userAutoAgree);
                    return;
                }
                if (processAutoAgree != null && processAutoAgree.size() > 0) {
                    isAutoAgree(processInstanceId, schemaId, workflowSchemaConfig, task, user, processAutoAgree);
                }
            }
        }
    }

    private void isAutoAgree(String processInstanceId, Long schemaId, WorkflowSchemaConfig workflowSchemaConfig, Task task, User user, List<Integer> autoAgreeConfig) {
        Object approveUserIdsObj = taskService.getVariable(task.getId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY);
        Object startUserIdObj = taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_START_USER_ID_KEY);
        List<Long> approveUserIds = Arrays.stream(Convert.toStr(approveUserIdsObj).split(StringPool.COMMA)).map(Convert::toLong).collect(Collectors.toList());
        Long startUserId = Convert.toLong(startUserIdObj);
        String opinion = StringPool.EMPTY;
        boolean isAgree = Boolean.FALSE;

        List<String> opinions = new ArrayList<>();//所有选择的自动同意规则
        List<Long> allUserIds = new ArrayList<>();//所有自动同意审批的用户id
        //新增流程发起流程记录
        WorkflowRecord record = new WorkflowRecord();
        //候选审批人包含流程任务发起人
        if (autoAgreeConfig.contains(WorkflowAutoAgreeType.APPROVED_INCLUDE_INITIATOR.getCode())) {
            if (approveUserIds.contains(startUserId)) {
                allUserIds.add(startUserId);
                taskService.createComment(task.getId(), task.getProcessInstanceId(), "候选审批人包含流程任务发起人---自动同意");
                //自动同意缓存流程审批人记录
                WorkFlowUtil.cacheTaskAssigneeRecord(processInstanceId, task.getId(), startUserId);
                opinion = "候选审批人包含流程任务发起人";
                opinions.add(opinion);
                isAgree = Boolean.TRUE;
            }
        }
        //候选审批人包含上一节点审批人
        if (autoAgreeConfig.contains(WorkflowAutoAgreeType.APPROVED_INCLUDE_PREV.getCode())) {
            //上一节点审批人 都是当前操作人。  因为此方法 只会在 发起  和  审批节点 执行
            if (approveUserIds.contains(StpUtil.getLoginIdAsLong())) {
                allUserIds.add(StpUtil.getLoginIdAsLong());
                taskService.createComment(task.getId(), task.getProcessInstanceId(), "候选审批人包含上一节点审批人---自动同意");
                //自动同意缓存流程审批人记录
                WorkFlowUtil.cacheTaskAssigneeRecord(processInstanceId, task.getId(), StpUtil.getLoginIdAsLong());
                opinion = "候选审批人包含上一节点审批人";
                opinions.add(opinion);
                isAgree = Boolean.TRUE;
            }
        }
        String approveName = "";
        if (autoAgreeConfig.contains(WorkflowAutoAgreeType.APPROVED_IN_PROCESS.getCode())) {

            Map<String, List<Long>> taskAssigneeRecordMap = redisUtil.get(processInstanceId + WorkflowConstant.TASK_ASSIGNEE_RECORD_VAR_KEY, new TypeReference<Map<String, List<Long>>>() {
            });

            //如果为空 代表是开始节点  还未有审批人
            if (taskAssigneeRecordMap == null) {
                if (approveUserIds.contains(Convert.toLong(startUserId))) {
                    allUserIds.add(Convert.toLong(startUserId));
                    taskService.createComment(task.getId(), task.getProcessInstanceId(), "候选审批人在此流程中审批过---自动同意");
                    WorkFlowUtil.cacheTaskAssigneeRecord(processInstanceId, task.getId(), startUserId);
                    opinion = "候选审批人在此流程中审批过";
                    opinions.add(opinion);
                    isAgree = Boolean.TRUE;
                }
            } else {
                Collection<List<Long>> assigneeList = taskAssigneeRecordMap.values();
                List<Long> allAssignee = new ArrayList<>();
                allAssignee.add(startUserId);
                for (List<Long> assignee : assigneeList) {
                    allAssignee.addAll(assignee);
                }
                //并集 就是在流程中审批过的人
                Collection<Long> intersection = CollectionUtil.intersection(allAssignee, approveUserIds);
                //获取自动审批的人员名称，给流程信息使用
                List<Long> list = intersection.stream().collect(Collectors.toList());
                allUserIds.addAll(list);
                if (intersection.size() > 0) {
                    //在流转到下一个任务前设置好流程信息时间
                    taskService.createComment(task.getId(), task.getProcessInstanceId(), "候选审批人在此流程中审批过---自动同意");
                    List<Long> assigneeInApprove = new ArrayList<>(intersection);
                    WorkFlowUtil.cacheTaskAssigneeRecord(processInstanceId, task.getId(), assigneeInApprove.get(0));
                    isAgree = Boolean.TRUE;
                    opinion = "候选审批人在此流程中审批过";
                    opinions.add(opinion);
                }
            }
        }
        if (isAgree) {
            //自动同意之后完成本次任务
            record.setRecordTime(LocalDateTime.now());
            taskService.complete(task.getId());
            List<Task> list = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
            String allOpinions = StringPool.EMPTY;
            if (opinions.size() > 0) {
                String chineseDot = "、";
                allOpinions = StrUtil.join(chineseDot, opinions);
            }
            approveName = WorkFlowUtil.getAllUserNamesByIds(allUserIds);
            //如果找不到任务了 默认流程已经结束
            if (list.size() > 0) {
                String allNextTaskName = StrUtil.join(StringPool.SPACE, list.parallelStream().map(t -> "【" + t.getName() + "】").collect(Collectors.toList()));

                String message = "【" + approveName + "】【自动同意】 审批, 审批意见为：“【" + allOpinions + "】”，由【" + task.getName() + "】流转到 " + allNextTaskName;
                addProcessRecord(task, schemaId, message, record);
                // 如果还有用户任务  则递归调用
                invokeAutoAgree(processInstanceId, schemaId, workflowSchemaConfig, list);
            } else {
                String message = "【" + approveName + "】【自动同意】 审批, 审批意见为：“【" + allOpinions + "】”，由【" + task.getName() + "】流转到 结束节点";
                addProcessRecord(task, schemaId, message, record);
            }
        }

    }


    /**
     * 判断是否由需要指定审批人 老版
     *
     * @param workflowSchemaConfig
     * @param processInstanceId
     * @return
     */
    private ImmutableTriple<String, Boolean, Boolean> isPrevChooseApproveBackUp(WorkflowSchemaConfig workflowSchemaConfig, String processInstanceId, List<Task> list) {

        //left==taskId | middle == 是否需要指定 | right == 是否多实例
        //如果一个任务都没有 证明了流程走完了  如果list 大于1  也证明任务的下一节点是多实例  多实例无法指定审批人
        if (list.size() == 0) {
            return new ImmutableTriple<>("", Boolean.FALSE, Boolean.FALSE);
        } else if (list.size() > 1) {
            return new ImmutableTriple<>("", Boolean.FALSE, Boolean.TRUE);
        } else {
            Task task = list.get(0);

            Optional<Map<String, Object>> userTaskConfigMap = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(task.getTaskDefinitionKey())).findFirst();
            if (!userTaskConfigMap.isPresent()) {
                throw new MyException("找不到下一个用户任务的配置!");
            }
            UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskConfigMap.get());
            //只有非会签流程  才需要判断 是否指定审批人 是否无对应处理人  判断完 前两者 再判断是否自动同意
            if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() == WorkflowMultiInstanceType.NONE.getCode()) {
                VariableInstance variableInstance = runtimeService.createVariableInstanceQuery().taskIdIn(task.getId()).variableName(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY).singleResult();
                //如果审批人变量不为空
                if (variableInstance != null) {
                    if (
                            userTaskConfig.getIsPrevChooseNext() == WorkflowIsPrevChooseNextType.PREV.getCode() ||
                                    workflowSchemaConfig.getProcessConfig().getIsPrevChooseNext() == WorkflowIsPrevChooseNextType.PREV.getCode()
                    ) {
                        return new ImmutableTriple<>(task.getId(), Boolean.TRUE, Boolean.FALSE);
                    }

                } else {
                    //
                    if (userTaskConfig.getNoHandler() == WorkflowNoHandlerType.PREV.getCode() ||
                            workflowSchemaConfig.getProcessConfig().getNoHandler() == WorkflowNoHandlerType.PREV.getCode()
                    ) {
                        return new ImmutableTriple<>(task.getId(), Boolean.TRUE, Boolean.FALSE);
                    }
                }
                return new ImmutableTriple<>(task.getId(), Boolean.FALSE, Boolean.FALSE);
            } else {
                return new ImmutableTriple<>("", Boolean.FALSE, Boolean.TRUE);
            }
        }
    }

    /**
     * 判断是否由需要指定审批人
     *
     * @param workflowSchemaConfig
     * @param list
     * @return
     */
    private List<LaunchAndApproveVo> isPrevChooseApprove(WorkflowSchemaConfig workflowSchemaConfig, List<Task> list, VariableMap variableMap) {
        List<LaunchAndApproveVo> voList = new ArrayList<>();

        //left==taskId | middle == 是否需要指定 | right == 是否多实例
        //如果一个任务都没有 证明了流程走完了  如果list 大于1  也证明任务的下一节点是多实例  多实例无法指定审批人
        if (list.size() == 0) {
            return voList;
        } else if (list.size() > 1) {
            String[] taskIds = list.stream().map(Task::getId).toArray(String[]::new);
            List<VariableInstance> variableInstanceList = runtimeService.createVariableInstanceQuery().taskIdIn(taskIds).variableName(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY).list();
            for (Task task : list) {
                LaunchAndApproveVo vo = new LaunchAndApproveVo();
                Optional<Map<String, Object>> userTaskConfigMap = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(task.getTaskDefinitionKey())).findFirst();
                if (!userTaskConfigMap.isPresent()) {
                    throw new MyException("找不到下一个用户任务的配置!");
                }
                UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskConfigMap.get());
                Optional<VariableInstance> variableInstanceOptional = variableInstanceList.stream()
                        .filter(v -> v.getTaskId().equals(task.getId()))
                        .filter(v -> v.getName().equals(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY))
                        .findFirst();

                //只有非会签流程  才需要判断 是否指定审批人 是否无对应处理人  判断完 前两者 再判断是否自动同意
                if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() == WorkflowMultiInstanceType.NONE.getCode()) {

                    //如果设置了指定审批人
                    if (
                            userTaskConfig.getIsPrevChooseNext() == WorkflowIsPrevChooseNextType.PREV.getCode() ||
                                    workflowSchemaConfig.getProcessConfig().getIsPrevChooseNext() == WorkflowIsPrevChooseNextType.PREV.getCode()
                    ) {
                        vo.setTaskName(task.getName());
                        vo.setTaskId(task.getId());
                        vo.setIsAppoint(Boolean.TRUE);
                        vo.setIsMultiInstance(Boolean.FALSE);
                        vo.setProvisionalApprover(userTaskConfig.getProvisionalApprover());
                        variableInstanceOptional.ifPresent(var -> {
                            vo.setApproveUserIds(Convert.toStr(var.getValue()));
                        });
                        voList.add(vo);


                        //如果是需要指定审批人 默认设置变量
                        taskService.setVariableLocal(task.getId(), WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.YES.getCode());
                        //默认定时10分钟  如果不指定审批人 就会使用原审批人
                        redisUtil.set(task.getId() + StringPool.UNDERSCORE + WorkflowConstant.TASK_IS_APPOINT_APPROVE, task.getId(), 10 * 60);
//                            redisUtil.set(GlobalConstant.CAPTCHA + StringPool.UNDERSCORE + mobile, code, 6000L);
                        continue;
                    }

                    //如果没有设置过审批人  需要走无对应处理人逻辑
                    if (userTaskConfig.getApproverConfigs() == null || userTaskConfig.getApproverConfigs().size() == 0) {
                        // 如果需要指定审批人
                        if (userTaskConfig.getNoHandler() == WorkflowNoHandlerType.PREV.getCode()) {
                            vo.setTaskName(task.getName());
                            vo.setTaskId(task.getId());
                            vo.setIsAppoint(Boolean.TRUE);
                            vo.setIsMultiInstance(Boolean.FALSE);
                            vo.setProvisionalApprover(userTaskConfig.getProvisionalApprover());

                            variableInstanceOptional.ifPresent(var -> {
                                vo.setApproveUserIds(Convert.toStr(var.getValue()));
                            });
                            voList.add(vo);

                            //如果是需要指定审批人 默认设置变量
                            taskService.setVariableLocal(task.getId(), WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.YES.getCode());
                            //默认定时10分钟  如果不指定审批人 就会使用原审批人
                            redisUtil.set(task.getId() + StringPool.UNDERSCORE + WorkflowConstant.TASK_IS_APPOINT_APPROVE, task.getId(), 10 * 60);
                            continue;
                        } else {
                            List<UserRoleRelation> userRoleRelations = redisUtil.get(GlobalConstant.USER_ROLE_RELATION_CACHE_KEY, new TypeReference<List<UserRoleRelation>>() {
                            });
                            //获取超级管理员成员
                            List<Long> adminUserIds = userRoleRelations.stream().filter(x -> x.getRoleId().equals(GlobalConstant.SUPER_ADMIN_ROLE_ID)).map(UserRoleRelation::getUserId).collect(Collectors.toList());
                            taskService.setVariableLocal(task.getId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StrUtil.join(StringPool.COMMA, adminUserIds));
                        }
                    }
                    vo.setTaskName(task.getName());
                    vo.setTaskId(task.getId());
                    vo.setIsAppoint(Boolean.FALSE);
                    vo.setIsMultiInstance(Boolean.FALSE);
                    vo.setProvisionalApprover(userTaskConfig.getProvisionalApprover());
                    taskService.setVariableLocal(task.getId(), WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode());
                    voList.add(vo);

                } else {
                    vo.setTaskName(task.getName());
                    vo.setTaskId(task.getId());
                    vo.setIsAppoint(Boolean.FALSE);
                    vo.setIsMultiInstance(Boolean.TRUE);
                    vo.setProvisionalApprover(userTaskConfig.getProvisionalApprover());
                    taskService.setVariableLocal(task.getId(), WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode());
                    voList.add(vo);
                }

                //记录一下需要发送消息(不需要指定审批人的)的任务id
//                if (variableInstanceOptional.isPresent()) {
//
//                    List<String> approveIds = ListUtil.toList(Convert.toStr(variableInstanceOptional.get().getValue()).split(StringPool.COMMA));
//
//                    NoticePolicyParam param = new NoticePolicyParam();
//                    param.setNoticeUserIds(Convert.toList(Long.class, approveIds));
//                    param.setTaskId(task.getId());
//                    param.setTaskName(task.getName());
//                    param.setProcessId(task.getProcessInstanceId());
//                    param.setTaskName(task.getName());
//                    param.setSchemaId(MapUtil.get(variableMap, WorkflowConstant.PROCESS_SCHEMA_ID_KEY, Long.class));
//                    param.setSchemaName(MapUtil.get(variableMap, WorkflowConstant.PROCESS_SCHEMA_NAME_KEY, String.class));
//                    param.setStartUserName(MapUtil.get(variableMap, WorkflowConstant.PROCESS_START_USER_NAME_KEY, String.class));
//
//                    param.setNoticePolicyConfigs(userTaskConfig.getNoticePolicyConfigs());
//                    WorkFlowUtil.sendApproveNoticePolicy(param, task.getName());
//                }

            }

            return voList;
        } else {
            Task task = list.get(0);

            LaunchAndApproveVo vo = new LaunchAndApproveVo();

            Optional<Map<String, Object>> userTaskConfigMap = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(task.getTaskDefinitionKey())).findFirst();
            if (!userTaskConfigMap.isPresent()) {
                throw new MyException("找不到下一个用户任务的配置!");
            }
            UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskConfigMap.get());
            VariableInstance variableInstance = runtimeService.createVariableInstanceQuery().taskIdIn(task.getId()).variableName(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY).singleResult();

            //只有非会签流程  才需要判断 是否指定审批人 是否无对应处理人  判断完 前两者 再判断是否自动同意
            if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() == WorkflowMultiInstanceType.NONE.getCode()) {

                if (
                        userTaskConfig.getIsPrevChooseNext() == WorkflowIsPrevChooseNextType.PREV.getCode() ||
                                workflowSchemaConfig.getProcessConfig().getIsPrevChooseNext() == WorkflowIsPrevChooseNextType.PREV.getCode()
                ) {
                    vo.setTaskName(task.getName());
                    vo.setTaskId(task.getId());
                    vo.setIsAppoint(Boolean.TRUE);
                    vo.setIsMultiInstance(Boolean.FALSE);
                    vo.setProvisionalApprover(userTaskConfig.getProvisionalApprover());
                    if (variableInstance != null) {
                        vo.setApproveUserIds(Convert.toStr(variableInstance.getValue()));
                    } else {
                        vo.setProvisionalApprover(Boolean.TRUE);
                    }

                    voList.add(vo);

                    //如果是需要指定审批人 默认设置变量
                    taskService.setVariableLocal(task.getId(), WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.YES.getCode());
                    //默认定时10分钟  如果不指定审批人 就会使用原审批人
                    redisUtil.set(task.getId() + StringPool.UNDERSCORE + WorkflowConstant.TASK_IS_APPOINT_APPROVE, task.getId(), 10 * 60);
                    return voList;
                }

                //如果没有设置过审批人  需要走无对应处理人逻辑
                if (userTaskConfig.getApproverConfigs() == null || userTaskConfig.getApproverConfigs().size() == 0) {

                    if (userTaskConfig.getNoHandler() == WorkflowNoHandlerType.PREV.getCode()
                    ) {
                        vo.setTaskName(task.getName());
                        vo.setTaskId(task.getId());
                        vo.setIsAppoint(Boolean.TRUE);
                        vo.setIsMultiInstance(Boolean.FALSE);
                        vo.setProvisionalApprover(userTaskConfig.getProvisionalApprover());

                        if (variableInstance != null) {
                            vo.setApproveUserIds(Convert.toStr(variableInstance.getValue()));
                        } else {
                            vo.setProvisionalApprover(Boolean.TRUE);
                        }
                        voList.add(vo);

                        //如果是需要指定审批人 默认设置变量
                        taskService.setVariableLocal(task.getId(), WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.YES.getCode());
                        //默认定时10分钟  如果不指定审批人 就会使用原审批人
                        redisUtil.set(task.getId() + StringPool.UNDERSCORE + WorkflowConstant.TASK_IS_APPOINT_APPROVE, task.getId(), 10 * 60);
                        return voList;
                    } else {
                        List<UserRoleRelation> userRoleRelations = redisUtil.get(GlobalConstant.USER_ROLE_RELATION_CACHE_KEY, new TypeReference<List<UserRoleRelation>>() {
                        });
                        //获取超级管理员成员
                        List<Long> adminUserIds = userRoleRelations.stream().filter(x -> x.getRoleId().equals(GlobalConstant.SUPER_ADMIN_ROLE_ID)).map(UserRoleRelation::getUserId).collect(Collectors.toList());
                        taskService.setVariableLocal(task.getId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StrUtil.join(StringPool.COMMA, adminUserIds));
                    }
                }
                vo.setTaskName(task.getName());
                vo.setTaskId(task.getId());
                vo.setIsAppoint(Boolean.FALSE);
                vo.setIsMultiInstance(Boolean.FALSE);
                vo.setProvisionalApprover(userTaskConfig.getProvisionalApprover());
                voList.add(vo);
                taskService.setVariableLocal(task.getId(), WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode());


            } else {
                vo.setTaskName(task.getName());
                vo.setTaskId(task.getId());
                vo.setIsAppoint(Boolean.FALSE);
                vo.setIsMultiInstance(Boolean.TRUE);
                vo.setProvisionalApprover(userTaskConfig.getProvisionalApprover());
                voList.add(vo);
                taskService.setVariableLocal(task.getId(), WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode());


            }
            //如果审批人变量不为空  并且不需要指定审批人 就发送消息
//            if (variableInstance != null) {
//                List<String> approveIds = ListUtil.toList(Convert.toStr(variableInstance.getValue()).split(StringPool.COMMA));
//
//                NoticePolicyParam param = new NoticePolicyParam();
//                param.setNoticeUserIds(Convert.toList(Long.class, approveIds));
//                param.setTaskId(task.getId());
//                param.setTaskName(task.getName());
//                param.setProcessId(task.getProcessInstanceId());
//                param.setTaskName(task.getName());
//                param.setSchemaId(MapUtil.get(variableMap, WorkflowConstant.PROCESS_SCHEMA_ID_KEY, Long.class));
//                param.setSchemaName(MapUtil.get(variableMap, WorkflowConstant.PROCESS_SCHEMA_NAME_KEY, String.class));
//                param.setStartUserName(MapUtil.get(variableMap, WorkflowConstant.PROCESS_START_USER_NAME_KEY, String.class));
//                param.setNoticePolicyConfigs(userTaskConfig.getNoticePolicyConfigs());
//                WorkFlowUtil.sendApproveNoticePolicy(param, task.getName());
//
//            }


            return voList;

        }
    }

    /**
     * 添加流程记录
     */
    private void addProcessRecord(Task task, Long schemaId, String message, WorkflowRecord record) {

        IWorkflowRecordService workflowRecordService = SpringUtil.getBean(IWorkflowRecordService.class);
        //新增流程发起流程记录
        record.setNodeId(task.getId());
        record.setNodeName(task.getName());
        record.setNodeType(WorkflowConstant.START_EVENT_TYPE_NAME);
        record.setProcessId(task.getProcessInstanceId());
        record.setSchemaId(schemaId);
        record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());

        record.setMessage(message);

        workflowRecordService.save(record);


    }


    /**
     * 记录开始节点
     *
     * @param workflowSchema
     * @param startNodeConfig
     * @param processInstance
     */
    private void startNodeRecord(WorkflowSchema workflowSchema, StartNodeConfig startNodeConfig, ProcessInstance processInstance, User user) {

        //新增流程发起流程记录
        WorkflowRecord startRecord = new WorkflowRecord();
        startRecord.setNodeId(startNodeConfig.getId());
        startRecord.setNodeName(startNodeConfig.getName());
        startRecord.setNodeType(WorkflowConstant.START_EVENT_TYPE_NAME);
        startRecord.setProcessId(processInstance.getId());
        startRecord.setSchemaId(workflowSchema.getId());
        startRecord.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
        startRecord.setRecordTime(LocalDateTime.now());
        startRecord.setMessage("【" + user.getName() + "】 创建流程");

        workflowRecordMapper.insert(startRecord);
    }

    /**
     * 构建流程默认参数 （全局变量 发起人id、发起人名、模板id 、模板名、页面配置参数值、流水号）等
     *
     * @param workflowSchema
     * @return
     */
    private VariableMap initDefaultParam(WorkflowSchema workflowSchema, Map<String, Object> processParam, long count) {

        User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());

        //设定全局变量 发起人id、发起人名、模板id 、模板名、页面配置参数值、流水号
        return Variables.createVariables()
                .putValue(WorkflowConstant.PROCESS_START_USER_ID_KEY, user.getId())
                .putValue(WorkflowConstant.PROCESS_START_USER_NAME_KEY, user.getName())
                .putValue(WorkflowConstant.PROCESS_START_USER_POST_ID_KEY, user.getPostId())
                .putValue(WorkflowConstant.PROCESS_SCHEMA_ID_KEY, workflowSchema.getId())
                .putValue(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY, workflowSchema.getName())
                .putValue(WorkflowConstant.PROCESS_PARAM_KEY, processParam)
                .putValue(WorkflowConstant.PROCESS_ISRECYCLE_FLAG_KEY, WorkflowIsRecycleType.NO.getCode())
                .putValue(WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY, count + 1);
    }

    /***
     * 表单赋值逻辑
     * @param formData 表单数据
     * @param workflowSchema
     * @param workflowSchemaConfig
     */
    private void initFormAssignment(Map<String, Map<String, Object>> formData, WorkflowSchema workflowSchema, WorkflowSchemaConfig workflowSchemaConfig, Map<String, Object> processParam) {
        //获取所有节点配置
        List<Map<String, Object>> childNodeConfig = workflowSchemaConfig.getChildNodeConfig();
        Optional<Map<String, Object>> startNodeMap = childNodeConfig.stream().filter(x -> x.containsValue(WorkflowConstant.BPMN_XML_START_EVENT_TYPE_NAME)).findFirst();

        if (!startNodeMap.isPresent()) {
            throw new MyException("当前任务没有开始节点");
        }
        //将map 转为 java类
        StartNodeConfig startNodeConfig = Convert.convert(StartNodeConfig.class, startNodeMap.get());


        WorkFlowUtil.invokeFormAssignment(formData, startNodeConfig.getAssignmentConfig(), startNodeConfig.getFormConfigs(), processParam);


    }

    /**
     * 构建传阅人
     *
     * @param task
     * @param circulateConfigs
     * @param workflowSchemaConfig
     * @param userTaskConfig
     */
    private String initCirculate(Task task, List<MemberConfig> circulateConfigs, WorkflowSchemaConfig workflowSchemaConfig, UserTaskConfig userTaskConfig, Map<String, Object> variableMaps) {
        //获取到需要追加的传阅人
        List<Long> appendUserTaskCirculated = WorkFlowUtil.getUserIdsByMemberConfig(circulateConfigs, workflowSchemaConfig.getChildNodeConfig(), task.getProcessInstanceId());

        List<WorkflowCirculated> circulatedList = circulatedService.list(Wrappers.lambdaQuery(WorkflowCirculated.class).eq(WorkflowCirculated::getTaskId, task.getId()));

        //获取当前用户的信息
        User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
        Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));

        String circulatedName = "";//添加的传阅人姓名
        String circulateMessage = "";//传阅信息
        //获取用户id对应的用户名
        circulatedName = getAllUserNamesByIds(appendUserTaskCirculated);
        if (circulatedList == null || circulatedList.size() == 0) {

            circulatedList = new ArrayList<>();
            for (Long userId : appendUserTaskCirculated) {
                WorkflowCirculated workflowCirculated = new WorkflowCirculated();
                workflowCirculated.setProcessId(task.getProcessInstanceId());
                workflowCirculated.setCreateTime(DateUtil.toLocalDateTime(task.getCreateTime()));
                workflowCirculated.setTaskId(task.getId());
                workflowCirculated.setCurrentProgress(userTaskConfig.getCurrentProgress());
                workflowCirculated.setTaskName(task.getName());
                workflowCirculated.setIsRead(YesOrNoEnum.NO.getCode());
                workflowCirculated.setSchemaId(MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
                workflowCirculated.setCirculatedUserId(userId);
                workflowCirculated.setStartUserId(MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_START_USER_ID_KEY));
                workflowCirculated.setSerialNumber(MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY));

                circulatedList.add(workflowCirculated);
            }
            circulatedService.saveBatch(circulatedList);
            //[操作人名称] 添加了传阅人[添加传人名称]，当前传阅人[所有传问人名称]
            if (StrUtil.isNotBlank(circulatedName)) {
                circulateMessage = "【" + user.getName() + "】添加了传阅人【" + circulatedName + "】，当前传阅人【" + circulatedName + "】";
            }
//            String message = "【" + user.getName() + "】添加了传阅人【" + circulatedName + "，当前传阅人【" + circulatedName + "】";
//            addProcessRecord(task, schemaId, message);
        } else {
            List<WorkflowCirculated> newList = new ArrayList<>();
            for (Long userId : appendUserTaskCirculated) {
                WorkflowCirculated workflowCirculated = BeanUtil.toBean(circulatedList.get(0), WorkflowCirculated.class);
                workflowCirculated.setCirculatedUserId(userId);
                workflowCirculated.setId(null);
                newList.add(workflowCirculated);
            }
            circulatedService.saveBatch(newList);
            //[操作人名称] 添加了传阅人[添加传人名称]，当前传阅人[所有传问人名称] = 需要追加的传阅人 + 原本的传阅人
            for (WorkflowCirculated workflowCirculated : circulatedList) {
                //将原本的传阅人加入到需要追加的传阅人中
                appendUserTaskCirculated.add(workflowCirculated.getCirculatedUserId());
            }
            String addCirculatedName = getAllUserNamesByIds(appendUserTaskCirculated); //增加后的当前传阅人
//            String message = "【" + user.getName() + "】添加了传阅人【" + circulatedName + "】，当前传阅人【" + addCirculatedName + "】";
//            addProcessRecord(task, schemaId, message);
            if (StrUtil.isNotBlank(addCirculatedName)) {
                circulateMessage = "【" + user.getName() + "】添加了传阅人【" + circulatedName + "】，当前传阅人【" + addCirculatedName + "】";
            }
        }
        return circulateMessage;
    }

    /**
     * 根据用户id获取对应的用户名，并进行拼接输出
     *
     * @param allUserIds 用户ids
     * @return 用户名（张三、李四）
     */
    private String getAllUserNamesByIds(List<Long> allUserIds) {
        String allDelegateUserNames = "";
        List<User> userList = redisUtil.get(GlobalConstant.USER_CACHE_KEY, new TypeReference<List<User>>() {
        });
        //如果缓存中不存在用户信息，就直接去数据库查询
        if (userList.size() == 0) {
            userList = userService.list();
            redisUtil.set(GlobalConstant.USER_CACHE_KEY, userList);
        }
        if (allUserIds.size() > 0) {
            //获取用户id对应的用户名
            List<String> allUserName = userList.stream().filter(u -> allUserIds.contains(u.getId())).map(User::getName).collect(Collectors.toList());
            if (allUserName.size() > 0) {
                allDelegateUserNames = StrUtil.join("、", allUserName);
            }
        }
        return allDelegateUserNames;
    }

    /**
     * 新增工作流程信息数据
     *
     * @param userTaskConfig   用户任务配置信息
     * @param nextTaskNameList 下一个节点的名称集合
     * @param user             当前用户的对象
     * @param record           工作流程信息的对象
     * @param oldTaskName      前一个节点的名称
     * @param buttonName       按钮名称
     */
    private void addWorkflowRecord(UserTaskConfig userTaskConfig, List<String> nextTaskNameList, User user, WorkflowRecord record, String oldTaskName, String buttonName, String approvedContent, Task task, Object resultName) {
        //单实例
        if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() == WorkflowMultiInstanceType.NONE.getCode()) {
            //用户节点到结束节点或者脚本节点，点击按钮时，也需要设置流程信息
            if (nextTaskNameList.size() == 0 && !buttonName.equals("")) {
                String message = "【" + user.getName() + "】【" + buttonName + "】审批,审批意见为：“【" + approvedContent + "】“";
                record.setMessage(message);
            }
            //单流向
            if (nextTaskNameList.size() == 1) {
                String message = "【" + user.getName() + "】【" + buttonName + "】审批,审批意见为：“【" + approvedContent + "】”,由【" + oldTaskName + "】 流转到【" + nextTaskNameList.get(0) + "】";
                record.setMessage(message);
            }//多流向
            if (nextTaskNameList.size() > 1) {
                String message = "【" + user.getName() + "】【" + buttonName + "】审批,审批意见为：“【" + approvedContent + "】”,由【" + oldTaskName + "】 流转到【" + nextTaskNameList.get(0) + "】";
                for (int i = 1; i < nextTaskNameList.size(); i++) {
                    message = message + "、【" + nextTaskNameList.get(i) + "】";
                }
                record.setMessage(message);
            }
        }
        //多实例，判断是不是会签流程，以及会签流程的状态
        else {
            //判断是否达到完成条件
            List<HistoricVariableInstance> list = historyService.createHistoricVariableInstanceQuery().processInstanceIdIn(task.getProcessInstanceId()).list();
            Boolean getCompleteConditional = isGetCompleteConditional(userTaskConfig, list);

            //审批但未达到完成条件;审批信息:[会签][用户名] [审批按钮名称]审批，审批意见为:“[审批意见]”。
            if (getCompleteConditional) {
                //获取当前节点是否完成会签或者会签失败
                if (ObjectUtil.isNotEmpty(resultName)) { //resultName只能从这个节点拿，不然会签节点后直接结束流程，就会拿不到流程变量信息
                    Boolean isSuccess = new Boolean(resultName.toString());
                    //审批达到完成条件,完成会签;审批信息:[会签][用户名] [审按名称]审批，审批见为:[审批意见]”，达到会签完成条件，由[上一节点名称]流转到下，多流向的情况则由[上一节点名称] 流转到[多流向节点名称]。
                    if (isSuccess) {
                        //用户节点到结束节点或者脚本节点，点击按钮时，也需要设置流程信息
                        if (nextTaskNameList.size() == 0 && !buttonName.equals("")) {
                            String message = "【会签】【" + user.getName() + "】【" + buttonName + "】审批,审批意见为：“【" + approvedContent + "】“,达到会签完成条件";
                            record.setMessage(message);
                        }
                        //单流向
                        if (nextTaskNameList.size() == 1) {
                            String message = "【会签】【" + user.getName() + "】【" + buttonName + "】审批,审批意见为：“【" + approvedContent + "】”,达到会签完成条件,由【" + oldTaskName + "】 流转到【" + nextTaskNameList.get(0) + "】";
                            record.setMessage(message);
                        }//多流向
                        if (nextTaskNameList.size() > 1) {
                            String message = "【会签】【" + user.getName() + "】【" + buttonName + "】审批,审批意见为：“【" + approvedContent + "】”,达到会签完成条件,由【" + oldTaskName + "】 流转到【" + nextTaskNameList.get(0) + "】";
                            for (int i = 1; i < nextTaskNameList.size(); i++) {
                                message = message + "、【" + nextTaskNameList.get(i) + "】";
                            }
                            record.setMessage(message);
                        }
                    }
                    //审批达到完成条件,会签失败;例如:审批信息:[会签][用户名][审按名称]审批，审批意见为:“[审批意见]”，未达到会签完成条件，由[管理层审批]流转到(部门审批]
                    if (buttonName.equals(WorkflowApproveType.DISAGREE.getValue())) {
                        //用户节点到结束节点或者脚本节点，点击按钮时，也需要设置流程信息
                        if (nextTaskNameList.size() == 0 && !buttonName.equals("")) {
                            String message = "【会签】【" + user.getName() + "】【" + buttonName + "】审批,审批意见为：“【" + approvedContent + "】“,未达到会签完成条件";
                            record.setMessage(message);
                        }
                        //单流向
                        if (nextTaskNameList.size() == 1) {
                            String message = "【会签】【" + user.getName() + "】【" + buttonName + "】审批,审批意见为：“【" + approvedContent + "】”,未达到会签完成条件,由【" + oldTaskName + "】 流转到【" + nextTaskNameList.get(0) + "】";
                            record.setMessage(message);
                        }//多流向
                        if (nextTaskNameList.size() > 1) {
                            String message = "【会签】【" + user.getName() + "】【" + buttonName + "】审批,审批意见为：“【" + approvedContent + "】”,未达到会签完成条件,由【" + oldTaskName + "】 流转到【" + nextTaskNameList.get(0) + "】";
                            for (int i = 1; i < nextTaskNameList.size(); i++) {
                                message = message + "、【" + nextTaskNameList.get(i) + "】";
                            }
                            record.setMessage(message);
                        }
                    }
                }
            } else {
                //审批但未达到完成条件;审批信息:[会签][用户名] [审批按钮名称]审批，审批意见为:“[审批意见]”。
                String message = "【会签】【" + user.getName() + "】【" + buttonName + "】审批,审批意见为：“【" + approvedContent + "】“";
                record.setMessage(message);
            }
        }
        workflowRecordMapper.insert(record);
    }

    /**
     * 判断多实例是否达到完成条件
     *
     * @param userTaskConfig
     * @param list
     * @return
     */
    private Boolean isGetCompleteConditional(UserTaskConfig userTaskConfig, List<HistoricVariableInstance> list) {
        int nrOfInstances = 0;
        int nrOfCompletedInstances = 0;
        int nrOfActiviteInstances = 0;
        for (HistoricVariableInstance historicVariableInstance : list) {
            //nrOfInstances:总共的实例数;
            if (historicVariableInstance.getName().equals(WorkflowConstant.MULTI_INSTANCE_VAR_KEY)) {
                nrOfInstances = Integer.parseInt(String.valueOf(historicVariableInstance.getValue()));
            }
            //nrOfCompletedInstances:已完成的实例数;
            if (historicVariableInstance.getName().equals(WorkflowConstant.MULTI_COMPLETED_INSTANCE_VAR_KEY)) {
                nrOfCompletedInstances = Integer.parseInt(String.valueOf(historicVariableInstance.getValue()));
            }
            //nrOfActiviteInstances;当前活动的实例数量，即还没有完成的实例数量
            if (historicVariableInstance.getName().equals(WorkflowConstant.MULTI_ACTIVE_INSTANCE_VAR_KEY)) {
                nrOfActiviteInstances = Integer.parseInt(String.valueOf(historicVariableInstance.getValue()));
            }
        }
        //获取流程参数中的完成条件,完成条件 0 全部  1 单个 2百分比
        if (userTaskConfig.getCountersignConfig().getFinishType() == 0) {
            if (nrOfActiviteInstances == 0) {
                return true;
            }
        } else if (userTaskConfig.getCountersignConfig().getFinishType() == 1) {
            if (nrOfCompletedInstances > 0) {
                return true;
            }
        } else if (userTaskConfig.getCountersignConfig().getFinishType() == 2) {
            //判断完成百分比是否达到
            if ((nrOfCompletedInstances / nrOfInstances) * 100 > userTaskConfig.getCountersignConfig().getPercentage()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Boolean dealTimeoutTask(Integer handleType, String taskId) {
        //根据taskid  获取任务信息
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

        if (task == null) {
            throw new MyException("找不到此任务！");
        }

        //获取当前任务变量中的  schemaId
        Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));

        //排除xml 查出数据
        WorkflowSchema workflowSchema = workflowSchemaMapper.selectOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, schemaId).select(x -> !x.getProperty().startsWith(DB_FIELD_XML_PREFIX)));

        //获取到整个流程模板的配置
        WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);
        //找到当前用户任务节点的配置信息
        Optional<Map<String, Object>> userTaskMapOptional = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(task.getTaskDefinitionKey())).findFirst();
        //判断是否找得到此任务节点 没有则报错
        if (!userTaskMapOptional.isPresent()) {
            throw new MyException("【任务id ：" + task.getId() + "】 用户任务节点配置有误，请联系管理员！");
        }

        //查出所有表单数据
        VariableMap variableMap = Variables.createVariables();

        List<LaunchAndApproveVo> result = new ArrayList<>();

        if (handleType == 1) {//自动驳回
            //获取到当前活动的实例
            ActivityInstance activityInstance = runtimeService.getActivityInstance(task.getProcessInstanceId());

            //获取所有已办任务节点
            List<HistoricActivityInstance> resultList = historyService
                    .createHistoricActivityInstanceQuery()
                    .processInstanceId(task.getProcessInstanceId())
                    .activityType("userTask")
                    .finished()
                    .orderByHistoricActivityInstanceEndTime()
                    .asc()
                    .list();

            if (null == resultList || resultList.size() == 0) {
                throw new MyException("当前任务之前不存在用户任务无法驳回！");
            }
            //得到第一个任务节点的id
            HistoricActivityInstance historicActivityInstance = resultList.get(0);
            String startActId = historicActivityInstance.getActivityId();
            if (startActId.equals(task.getTaskDefinitionKey())) {
                throw new MyException("开始节点无法驳回！");
            }

            //得到上一个任务节点的ActivityId和待办人
            Map<String, String> lastNode = getLastNode(resultList, task.getTaskDefinitionKey());
            if (null == lastNode) {
                throw new MyException("回退节点异常！");
            }
            String toActId = lastNode.get("toActId");
            String assignee = lastNode.get("assignee");
            //设置流程中的可变参数
            Map<String, Object> taskVariable = new HashMap<>(2);
            taskVariable.put("user", assignee);
            taskService.createComment(task.getId(), task.getProcessInstanceId(), "驳回原因:" + "自动驳回");

            //先停止当前活动示例  然后撤回流程到上个节点
            runtimeService.createProcessInstanceModification(task.getProcessInstanceId())
                    .cancelActivityInstance(getInstanceIdForActivity(activityInstance, task.getTaskDefinitionKey()))//关闭相关任务
                    .setAnnotation("系统" + "】 自动驳回 【任务:" + task.getId() + "】")
                    .startBeforeActivity(toActId) //上一个节点
                    .setVariables(taskVariable)//流程的可变参数赋值
                    .execute();

            Optional<Map<String, Object>> rejectNodeMap = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(toActId)).findFirst();

            rejectNodeMap.ifPresent(node -> {
                //将map 转为 java类
                UserTaskConfig rejectUserTaskConfig = Convert.convert(UserTaskConfig.class, node);

                //新增流程发起流程记录
                WorkflowRecord record = new WorkflowRecord();
                record.setNodeId(task.getId());
                record.setNodeName(task.getName());
                record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
                record.setProcessId(task.getProcessInstanceId());
                record.setSchemaId(schemaId);
                record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
                record.setRecordTime(LocalDateTime.now().minusSeconds(+1));//时间设置提前1秒钟，好排序

//                    record.setMessage("【审批人:" + user.getName() + "】 将 【任务:" + task.getName() + "】 驳回到 【任务:" + rejectUserTaskConfig.getName() + "】");
                record.setMessage("审批信息：由于审批超时，【系统】【自动驳回】审批,审批意见为：“系统自动驳回审批”，由【" + task.getName() + "】 流转到【" + rejectUserTaskConfig.getName() + "】");
                workflowRecordMapper.insert(record);
            });
        } else if (handleType == 2) {//同意
            //新增流程发起流程记录
            WorkflowRecord record = new WorkflowRecord();
            record.setNodeId(task.getId());
            record.setNodeName(task.getName());
            record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
            record.setProcessId(task.getProcessInstanceId());
            record.setSchemaId(schemaId);
            record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
            //时间设值必须写在complete上面，防止用户任务节点监听时的流程信息，在次任务前面。
            record.setRecordTime(LocalDateTime.now());
            //当前任务的名称
            String oldTaskName = task.getName();

            taskService.complete(task.getId());
            List<Task> list = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
            result = isPrevChooseApprove(workflowSchemaConfig, list, variableMap);

            //下一个节点的任务名称集合
            List<String> nextTaskNameList = list.stream().map(Task::getName).collect(Collectors.toList());

            //如果不需要指定审批人 默认走自动同意规则
//            invokeAutoAgree(task.getProcessInstanceId(), workflowSchema.getId(), workflowSchemaConfig, list);

            //用户节点到结束节点或者脚本节点，点击按钮时，也需要设置流程信息
            if (nextTaskNameList.size() == 0) {
                String message = "审批信息：由于审批超时，【系统】【自动同意】审批,审批意见为：“系统自动同意审批”，由【" + oldTaskName + "】 流转到【" + "结束节点" + "】";
                record.setMessage(message);
            }
            //单流向
            if (nextTaskNameList.size() == 1) {
                String message = "审批信息：由于审批超时，【系统】【自动同意】审批,审批意见为：“系统自动同意审批”，由【" + oldTaskName + "】 流转到【" + nextTaskNameList.get(0) + "】";
                record.setMessage(message);
            }//多流向
            if (nextTaskNameList.size() > 1) {
                String message = "审批信息：由于审批超时，【系统】【自动同意】审批,审批意见为：“系统自动同意审批”，由【" + oldTaskName + "】 流转到【" + nextTaskNameList.get(0) + "】";
                for (int i = 1; i < nextTaskNameList.size(); i++) {
                    message = message + "、【" + nextTaskNameList.get(i) + "】";
                }
                record.setMessage(message);
            }

            workflowRecordMapper.insert(record);
        }
        return true;
    }

    /**
     * 获取上一节点信息
     * 分两种情况：
     * 1、当前节点不在历史节点里
     * 2、当前节点在历史节点里
     * 比如，resultList={1,2,3}
     * (1)当前节点是4，表示3是完成节点，4驳回需要回退到3
     * (2)当前节点是2，表示3是驳回节点，3驳回到当前2节点，2驳回需要回退到1
     * 其他驳回过的情况也都包含在情况2中。
     *
     * @param resultList        历史节点列表
     * @param currentActivityId 当前待办节点ActivityId
     * @return 返回值：上一节点的ActivityId和待办人（toActId, assignee）
     */
    private static Map<String, String> getLastNode(List<HistoricActivityInstance> resultList, String currentActivityId) {
        Map<String, String> backNode = new HashMap<>();
        //新建一个有序不重复集合
        LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap();
        for (HistoricActivityInstance hai : resultList) {
            linkedHashMap.put(hai.getActivityId(), hai.getAssignee());
        }
        //分两种情况：当前节点在不在历史节点里面，当前节点在历史节点里
        //情况1、当前节点不在历史节点里
        int originSize = resultList.size();
        int duplicateRemovalSize = linkedHashMap.size();
        //判断历史节点中是否有重复节点
        //if(originSize == duplicateRemovalSize){
        boolean flag = false;
        for (Map.Entry entry : linkedHashMap.entrySet()) {
            if (currentActivityId.equals(entry.getKey())) {
                flag = true;
                break;
            }
        }
        if (!flag) {
            //当前节点不在历史节点里：最后一个节点是完成节点
            HistoricActivityInstance historicActivityInstance = resultList.get(originSize - 1);
            backNode.put("toActId", historicActivityInstance.getActivityId());
            backNode.put("assignee", historicActivityInstance.getAssignee());
            return backNode;
        }
        //情况2、当前节点在历史节点里（已回退过的）
        return currentNodeInHis(linkedHashMap, currentActivityId);
    }

    private String getInstanceIdForActivity(ActivityInstance activityInstance, String activityId) {
        ActivityInstance instance = getChildInstanceForActivity(activityInstance, activityId);
        if (instance != null) {
            return instance.getId();
        }
        return null;
    }

    private ActivityInstance getChildInstanceForActivity(ActivityInstance activityInstance, String activityId) {
        if (activityId.equals(activityInstance.getActivityId())) {
            return activityInstance;
        }
        for (ActivityInstance childInstance : activityInstance.getChildActivityInstances()) {
            ActivityInstance instance = getChildInstanceForActivity(childInstance, activityId);
            if (instance != null) {
                return instance;
            }
        }
        return null;
    }

    private static Map<String, String> currentNodeInHis(LinkedHashMap<String, String> linkedHashMap, String currentActivityId) {
        //情况2、当前节点在历史节点里（已回退过的）
        Map<String, String> backNode = new HashMap<>();
        ListIterator<Map.Entry<String, String>> li = new ArrayList<>(linkedHashMap.entrySet()).listIterator();
        //System.out.println("已回退过的");
        while (li.hasNext()) {
            Map.Entry<String, String> entry = li.next();
            if (currentActivityId.equals(entry.getKey())) {
                li.previous();
                Map.Entry<String, String> previousEntry = li.previous();
                backNode.put("toActId", previousEntry.getKey());
                backNode.put("assignee", previousEntry.getValue());
                return backNode;
            }
        }
        return null;
    }
}
