package com.qmth.sop.business.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.sop.business.activiti.service.ActivitiService;
import com.qmth.sop.business.bean.dto.DataPermissionDto;
import com.qmth.sop.business.bean.dto.OrgUserNameDto;
import com.qmth.sop.business.bean.dto.UserArchivesDto;
import com.qmth.sop.business.bean.params.*;
import com.qmth.sop.business.bean.result.*;
import com.qmth.sop.business.entity.*;
import com.qmth.sop.business.mapper.TBSopInfoMapper;
import com.qmth.sop.business.service.*;
import com.qmth.sop.common.contant.SystemConstant;
import com.qmth.sop.common.enums.*;
import com.qmth.sop.common.lock.MemoryLock;
import com.qmth.sop.common.util.GsonUtil;
import com.qmth.sop.common.util.JacksonUtil;
import com.qmth.sop.common.util.ResultUtil;
import com.qmth.sop.common.util.ServletUtil;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * sop表 服务实现类
 * </p>
 *
 * @author wangliang
 * @since 2023-08-01
 */
@Service
public class TBSopInfoServiceImpl extends ServiceImpl<TBSopInfoMapper, TBSopInfo> implements TBSopInfoService {

    private final static Logger log = LoggerFactory.getLogger(TBSopInfoServiceImpl.class);

    @Resource
    TBSopInfoMapper tbSopInfoMapper;

    @Resource
    ActivitiService activitiService;

    @Resource
    TFCustomFlowEntityService tfCustomFlowEntityService;

    @Resource
    TaskService taskService;

    @Resource
    TBSopInfoService tbSopInfoService;

    @Resource
    TBCrmService tbCrmService;

    @Resource
    TFCustomFlowService tfCustomFlowService;

    @Resource
    TSJobRemindService tsJobRemindService;

    @Resource
    TFFlowApproveService tfFlowApproveService;

    @Resource
    SysUserService sysUserService;

    @Value("${db.name}")
    String databaseName;

    @Resource
    TFFlowLogService tfFlowLogService;

    @Resource
    TBServiceService tbServiceService;

    @Resource
    TBSopPlanDateLogService tbSopPlanDateLogService;

    @Resource
    TBSopLogService tbSopLogService;

    @Resource
    TBCrmDetailService tbCrmDetailService;

    @Resource
    TBUserArchivesService tbUserArchivesService;

    @Resource
    MemoryLock memoryLock;

    @Resource
    TBUserArchivesAllocationService tbUserArchivesAllocationService;

    @Resource
    TBDingService tbDingService;

    @Resource
    TBDingSubmitService tbDingSubmitService;

    @Resource
    TBDeviceDeliveryService tbDeviceDeliveryService;

    /**
     * 查询动态sop表名是否存在
     *
     * @param db
     * @param tableName
     * @return
     */
    @Override
    public int selectSopDynamicExist(String db, String tableName) {
        return this.baseMapper.selectSopDynamicExist(db, tableName);
    }

    /**
     * 保存动态sop信息
     *
     * @param tfCustomFlow
     * @param code
     * @param flowId
     * @param flowTaskResult
     * @return
     */
    @Override
    @Transactional
    public Boolean saveDynamicSop(TFCustomFlow tfCustomFlow, String code, Long flowId, FlowTaskResult flowTaskResult) {
        String tableName = tfCustomFlow.getType().getTableName() + "_" + tfCustomFlow.getVersion();
        List<FlowFormWidgetResult> flowFormWidgetResultList = flowTaskResult.getFormProperty();
        Map<String, String> formFieldMap = new HashMap<>(flowFormWidgetResultList.size());
        for (FlowFormWidgetResult f : flowFormWidgetResultList) {
            if (f.getCode() != WidgetCodeEnum.FORM_GROUP_TITLE && f.getCode() != WidgetCodeEnum.ONLY_TITLE
                    && f.getCode() != WidgetCodeEnum.BUTTON && f.getCode() != WidgetCodeEnum.IMG
                    && f.getCode() != WidgetCodeEnum.LABEL && f.getCode() != WidgetCodeEnum.SIGN
                    && f.getCode() != WidgetCodeEnum.OTHER) {
                if (f.getInputType() != WidgetInputTypeEnum.ARRAY) {
                    if (Objects.nonNull(f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                        JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                        String value = jsonObject.getString(SystemConstant.VALUE);
                        formFieldMap.put(f.getFormId(), value);
                    }
                } else {
                    if (Objects.nonNull(f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                        JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                        JSONArray jsonArray = jsonObject.getJSONArray(SystemConstant.VALUE);
                        StringJoiner stringJoiner = new StringJoiner(",");
                        for (int i = 0; i < jsonArray.size(); i++) {
                            stringJoiner.add(jsonArray.getString(i));
                        }
                        formFieldMap.put(f.getFormId(), stringJoiner.toString());
                    }
                }
            }
        }
        //首先查询动态表里是否有当前表单的数据，没有插入，有则更新
        int count = this.selectDynamicSop(tableName, tfCustomFlow.getFlowDeploymentId(), flowId);
        if (count == 0) {//插入操作
            //查询所有列
            List<Map<String, String>> dynamicFieldList = this.baseMapper.selectDynamicSopAll(databaseName, tableName);
            Map<String, Object> map = new LinkedHashMap<>();
            if (!CollectionUtils.isEmpty(dynamicFieldList)) {
                StringJoiner stringJoinerFieldName = new StringJoiner(",");
                StringJoiner stringJoinerFieldValue = new StringJoiner(",");
                for (Map<String, String> m : dynamicFieldList) {
                    for (Map.Entry<String, String> entry : m.entrySet()) {
                        switch (entry.getValue()) {
                        case SystemConstant.TYPE:
                            map.put(entry.getValue(), tfCustomFlow.getType());
                            break;
                        case SystemConstant.FLOW_DEPLOYMENT_ID_FIELD:
                            map.put(entry.getValue(), tfCustomFlow.getFlowDeploymentId());
                            break;
                        case SystemConstant.VERSION:
                            map.put(entry.getValue(), tfCustomFlow.getVersion());
                            break;
                        case SystemConstant.CODE:
                            map.put(entry.getValue(), code);
                            break;
                        case SystemConstant.FLOW_ID:
                            map.put(entry.getValue(), flowId);
                            break;
                        default:
                            if (formFieldMap.containsKey(entry.getValue())) {
                                map.put(entry.getValue(), formFieldMap.get(entry.getValue()));
                            } else {
                                map.put(entry.getValue(), null);
                            }
                            break;
                        }
                    }
                }
                for (Map.Entry<String, Object> entry : map.entrySet()) {
                    stringJoinerFieldName.add(entry.getKey());
                    stringJoinerFieldValue.add(
                            Objects.nonNull(entry.getValue()) ? "'" + entry.getValue().toString() + "'" : null);
                }
                tbSopInfoMapper.saveDynamicSop(tableName, stringJoinerFieldName.toString(),
                        stringJoinerFieldValue.toString());
            }
        } else {//否则更新
            if (!CollectionUtils.isEmpty(formFieldMap)) {
                StringJoiner stringJoinerFieldValue = new StringJoiner(",");
                for (Map.Entry<String, String> entry : formFieldMap.entrySet()) {
                    stringJoinerFieldValue.add(entry.getKey() + "='" + entry.getValue() + "'");
                }
                tbSopInfoMapper.updateDynamicSop(tableName, tfCustomFlow.getFlowDeploymentId(), flowId,
                        stringJoinerFieldValue.toString());
            }
        }
        return true;
    }

    /**
     * 查询动态sop数据
     *
     * @param tableName
     * @param flowDeploymentId
     * @param flowId
     * @return
     */
    @Override
    public int selectDynamicSop(String tableName, String flowDeploymentId, Long flowId) {
        return this.baseMapper.selectDynamicSop(tableName, flowDeploymentId, flowId);
    }

    /**
     * sop发布信息
     *
     * @param sopPublishParam
     * @return
     */
    @Override
    @Transactional
    public String sopPublish(SopPublishParam sopPublishParam) throws Exception {
        Objects.requireNonNull(sopPublishParam.getCrmNo(), "crm编号不能为空");

        if (sopPublishParam.getApprove() != FlowApprovePassEnum.DRAFT) {
            throw ExceptionResultEnum.PARAMS_ERROR.exception("sop发布只能为草稿");
        }
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        CrmProjectResult crmProjectResult = tbCrmService.findCrmProjectInfo(sopPublishParam.getSopNo(),
                sopPublishParam.getCrmDetailId(), sopPublishParam.getCrmNo());
        Optional.ofNullable(crmProjectResult).orElseThrow(() -> ExceptionResultEnum.CRM_NO_NO_DATA.exception());

        Objects.requireNonNull(crmProjectResult.getRegionManagerId(), "未设置大区经理");
        if (CollectionUtils.isEmpty(crmProjectResult.getProjectManagerList())) {
            throw ExceptionResultEnum.ERROR.exception("未设置项目经理");
        }
        if (CollectionUtils.isEmpty(crmProjectResult.getEngineerList())) {
            throw ExceptionResultEnum.ERROR.exception("未设置工程师");
        }

        TBService tbService = tbServiceService.getById(crmProjectResult.getServiceUnitId());
        Optional.ofNullable(tbService).orElseThrow(() -> ExceptionResultEnum.SERVICE_NO_DATA.exception());
        Optional.ofNullable(tbService.getStatus()).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("服务单元状态为空"));

        if (Objects.isNull(tbService.getEnable()) || !tbService.getEnable()) {
            throw ExceptionResultEnum.ERROR.exception("服务单元已作废");
        }
        if (tbService.getStatus() != ServiceStatusEnum.PUBLISH) {
            throw ExceptionResultEnum.ERROR.exception("只有发布状态的服务单元才能新增sop");
        }

        TFCustomFlow tfCustomFlow = tfCustomFlowService.findByFlowDeploymentId(sopPublishParam.getFlowDeploymentId());
        TFCustomFlow maxTfCustomFlow = tfCustomFlowService.findMaxVersion(null, null, tfCustomFlow.getType());
        Optional.ofNullable(maxTfCustomFlow).orElseThrow(() -> ExceptionResultEnum.FLOW_CUSTOM_NO_DATA.exception());
        if (maxTfCustomFlow.getVersion().intValue() != tfCustomFlow.getVersion().intValue()) {//说明版本发生变化
            BeanUtils.copyProperties(maxTfCustomFlow, tfCustomFlow);
        }

        if (tfCustomFlow.getType() != TFCustomTypeEnum.OFFICE_SOP_FLOW
                && tfCustomFlow.getType() != TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW) {
            throw ExceptionResultEnum.ERROR.exception("sop流程类型错误");
        }

        if (tfCustomFlow.getType() == TFCustomTypeEnum.OFFICE_SOP_FLOW) {
            List<String> approveUserIds = new ArrayList<>(crmProjectResult.getProjectManagerList().size());
            crmProjectResult.getProjectManagerList().stream().peek(e -> approveUserIds.add(e.getUserId().toString()))
                    .collect(Collectors.toList());
            sopPublishParam.setApproveUserIds(approveUserIds);
        } else if (tfCustomFlow.getType() == TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW) {
            sopPublishParam.setApproveUserIds(
                    new ArrayList(Arrays.asList(crmProjectResult.getRegionManagerId().toString())));
        }

        sopPublishParam = tbSopInfoService.setSetupOne(sopPublishParam, tfCustomFlow, crmProjectResult);
        Map<String, Object> map = activitiService.taskApprove(new FlowApproveParam(sopPublishParam));
        tfCustomFlow = Objects.isNull(tfCustomFlow) ? (TFCustomFlow) map.get(SystemConstant.FLOW_CUSTOM) : tfCustomFlow;
        TFCustomFlowEntity tfCustomFlowEntity = (TFCustomFlowEntity) map.get(SystemConstant.FLOW_ENTITY);
        TBSopInfo tbSopInfo = new TBSopInfo(sopPublishParam.getCrmNo(), tfCustomFlowEntity.getCode(),
                crmProjectResult.getServiceUnitId(), crmProjectResult.getCustomId(), crmProjectResult.getProductId(),
                tfCustomFlow.getType(), SopStatusEnum.valueOf(sopPublishParam.getApprove().name()), sysUser.getId());

        map.put(SystemConstant.CRM_INFO, crmProjectResult);
        map.put(SystemConstant.SOP_ID, tbSopInfo.getId());
        tbSopInfoService.saveJobRemind(map);
        tfCustomFlowEntity.setObjId(tbSopInfo.getId());
        tfCustomFlowEntityService.updateById(tfCustomFlowEntity);
        tbCrmService.updateCrmStatus(sopPublishParam.getCrmNo(), CrmStatusEnum.PUBLISH);
        tbSopInfoService.save(tbSopInfo);
        return tfCustomFlowEntity.getCode();
    }

    /**
     * 设置流程第一步值
     *
     * @param sopPublishParam
     * @param tfCustomFlow
     * @param crmProjectResult
     * @throws Exception
     */
    @Override
    public SopPublishParam setSetupOne(SopPublishParam sopPublishParam, TFCustomFlow tfCustomFlow,
            CrmProjectResult crmProjectResult) throws Exception {
        Map<String, Object> propertiesMap = activitiService.formPropertiesGet(sopPublishParam.getFlowDeploymentId(),
                null, null, false, sopPublishParam.getCrmNo());
        Map<String, Object> var = sopPublishParam.getVar();
        TBCrmDetail tbCrmDetail = (TBCrmDetail) var.get(SystemConstant.CRM_DETAIL);
        String processVar = (String) var.get(SystemConstant.PROCESS_VAR);
        if (!CollectionUtils.isEmpty(propertiesMap)) {
            FlowTaskResult flowTaskResult = (FlowTaskResult) propertiesMap.get(SystemConstant.FORM_PROPERTIES);
            flowTaskResult = tbSopInfoService.updateSetupOne(flowTaskResult, tfCustomFlow, processVar, tbCrmDetail,
                    crmProjectResult);
            sopPublishParam.setFormProperties(JacksonUtil.parseJson(flowTaskResult));
        }
        return sopPublishParam;
    }

    /**
     * 修改流程第一步值
     *
     * @param flowTaskResult
     * @param tfCustomFlow
     * @param processVar
     * @param tbCrmDetail
     * @param crmProjectResult
     * @return
     */
    @Override
    public FlowTaskResult updateSetupOne(FlowTaskResult flowTaskResult, TFCustomFlow tfCustomFlow, String processVar,
            TBCrmDetail tbCrmDetail, CrmProjectResult crmProjectResult) {
        List<FlowFormWidgetResult> flowFormWidgetResultList = flowTaskResult.getFormProperty();
        if (!CollectionUtils.isEmpty(flowFormWidgetResultList)) {
            if (tfCustomFlow.getType() == TFCustomTypeEnum.OFFICE_SOP_FLOW) {
                Map<Long, FlowFormWidgetResult> flowFormWidgetResultMap = null;
                if (Objects.nonNull(processVar)) {
                    List<FlowFormWidgetResult> flowFormWidgetResults = JSONArray.parseArray(processVar,
                            FlowFormWidgetResult.class);
                    flowFormWidgetResultMap = flowFormWidgetResults.stream().collect(
                            Collectors.toMap(FlowFormWidgetResult::getId, Function.identity(), (dto1, dto2) -> dto1));
                }
                for (FlowFormWidgetResult f : flowFormWidgetResultList) {
                    if (!CollectionUtils.isEmpty(flowFormWidgetResultMap) && flowFormWidgetResultMap.containsKey(
                            f.getId())) {
                        f.setValue(flowFormWidgetResultMap.get(f.getId()).getValue());
                    } else if (Objects.nonNull(tbCrmDetail) && Objects.nonNull(tbCrmDetail.getContacts())
                            && f.getFormId().contains(ProcessLimitedEnum.CONTACTS.getKey())) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put(SystemConstant.VALUE, tbCrmDetail.getContacts());
                        f.setValue(jsonObject.toJSONString());
                    } else if (Objects.nonNull(tbCrmDetail) && Objects.nonNull(tbCrmDetail.getMobileNumber())
                            && f.getFormId().contains(ProcessLimitedEnum.MOBILE_NUMBER.getKey())) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put(SystemConstant.VALUE, tbCrmDetail.getMobileNumber());
                        f.setValue(jsonObject.toJSONString());
                    } else if (Objects.nonNull(tbCrmDetail) && f.getFormId()
                            .contains(ProcessLimitedEnum.SERVICE_SCOPE_RADIO.getKey())) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put(SystemConstant.VALUE, tbCrmDetail.getServiceScope());
                        f.setValue(jsonObject.toJSONString());
                    } else if (Objects.nonNull(tbCrmDetail) && f.getFormId()
                            .contains(ProcessLimitedEnum.SCAN_START_TIME.getKey())) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put(SystemConstant.VALUE, tbCrmDetail.getScanStartTime());
                        f.setValue(jsonObject.toJSONString());
                    } else if (Objects.nonNull(tbCrmDetail) && f.getFormId()
                            .contains(ProcessLimitedEnum.SCAN_END_TIME.getKey())) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put(SystemConstant.VALUE, tbCrmDetail.getScanEndTime());
                        f.setValue(jsonObject.toJSONString());
                    } else if (Objects.nonNull(tbCrmDetail) && f.getFormId()
                            .contains(ProcessLimitedEnum.MARK_START_TIME.getKey())) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put(SystemConstant.VALUE, tbCrmDetail.getMarkPaperStartTime());
                        f.setValue(jsonObject.toJSONString());
                    } else if (Objects.nonNull(tbCrmDetail) && f.getFormId()
                            .contains(ProcessLimitedEnum.MARK_END_TIME.getKey())) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put(SystemConstant.VALUE, tbCrmDetail.getMarkPaperEndTime());
                        f.setValue(jsonObject.toJSONString());
                    } else if (Objects.nonNull(tbCrmDetail) && f.getFormId()
                            .contains(ProcessLimitedEnum.REGION_MANAGER_ID.getKey())) {
                        if (Objects.nonNull(crmProjectResult.getRegionManagerId())) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE, crmProjectResult.getRegionManagerId().toString());
                            f.setValue(jsonObject.toJSONString());
                        }
                    } else if (Objects.nonNull(tbCrmDetail) && f.getFormId()
                            .contains(ProcessLimitedEnum.ENGINEER_USERS_ID.getKey())) {
                        if (!CollectionUtils.isEmpty(crmProjectResult.getEngineerList())) {
                            List<String> list = new ArrayList<>(crmProjectResult.getEngineerList().size());
                            crmProjectResult.getEngineerList().stream().peek(s -> list.add(s.getUserId().toString()))
                                    .collect(Collectors.toList());
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE, list);
                            f.setValue(jsonObject.toJSONString());
                        }
                    } else if (Objects.nonNull(tbCrmDetail) && f.getFormId()
                            .contains(ProcessLimitedEnum.PROJECT_MANAGER_ID.getKey())) {
                        if (!CollectionUtils.isEmpty(crmProjectResult.getProjectManagerList())) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE,
                                    crmProjectResult.getProjectManagerList().get(0).getUserId().toString());
                            f.setValue(jsonObject.toJSONString());
                        }
                    } else if (Objects.nonNull(tbCrmDetail) && f.getFormId()
                            .contains(ProcessLimitedEnum.REGION_COORDINATOR_ID.getKey())) {
                        if (!CollectionUtils.isEmpty(crmProjectResult.getRegionCoordinatorList())) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE,
                                    crmProjectResult.getRegionCoordinatorList().get(0).getUserId().toString());
                            f.setValue(jsonObject.toJSONString());
                        }
                    }
                }
            } else if (tfCustomFlow.getType() == TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW) {
                for (FlowFormWidgetResult f : flowFormWidgetResultList) {
                    if (Objects.nonNull(tbCrmDetail) && f.getFormId()
                            .contains(ProcessLimitedEnum.REGION_MANAGER_ID.getKey())) {
                        if (Objects.nonNull(crmProjectResult.getRegionManagerId())) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE, crmProjectResult.getRegionManagerId().toString());
                            f.setValue(jsonObject.toJSONString());
                        }
                    } else if (Objects.nonNull(tbCrmDetail) && f.getFormId()
                            .contains(ProcessLimitedEnum.ENGINEER_USERS_ID.getKey())) {
                        if (!CollectionUtils.isEmpty(crmProjectResult.getEngineerList())) {
                            List<String> list = new ArrayList<>(crmProjectResult.getEngineerList().size());
                            crmProjectResult.getEngineerList().stream().peek(s -> list.add(s.getUserId().toString()))
                                    .collect(Collectors.toList());
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE, list);
                            f.setValue(jsonObject.toJSONString());
                        }
                    } else if (Objects.nonNull(tbCrmDetail) && f.getFormId()
                            .contains(ProcessLimitedEnum.PROJECT_MANAGER_ID.getKey())) {
                        if (!CollectionUtils.isEmpty(crmProjectResult.getProjectManagerList())) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE,
                                    crmProjectResult.getProjectManagerList().get(0).getUserId().toString());
                            f.setValue(jsonObject.toJSONString());
                        }
                    } else if (Objects.nonNull(tbCrmDetail) && f.getFormId()
                            .contains(ProcessLimitedEnum.REGION_COORDINATOR_ID.getKey())) {
                        if (!CollectionUtils.isEmpty(crmProjectResult.getRegionCoordinatorList())) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE,
                                    crmProjectResult.getRegionCoordinatorList().get(0).getUserId().toString());
                            f.setValue(jsonObject.toJSONString());
                        }
                    }
                }
            }
        }
        return flowTaskResult;
    }

    /**
     * sop转审
     *
     * @param crmDetailId
     * @param sopAllocationParam
     * @return
     * @throws InterruptedException
     */
    @Override
    @Transactional
    public Boolean sopApproverExchange(Long crmDetailId, SopAllocationParam sopAllocationParam)
            throws InterruptedException {
        TBCrmDetail tbCrmDetail = tbCrmDetailService.getById(crmDetailId);
        Objects.requireNonNull(tbCrmDetail, "未找到派单sop信息");
        if (Objects.nonNull(tbCrmDetail.getSopNo())) {
            //已发布且状态为草稿的sop可以修改
            TFCustomFlowEntity tfCustomFlowEntity = tfCustomFlowEntityService.findByCode(tbCrmDetail.getSopNo());
            Optional.ofNullable(tfCustomFlowEntity)
                    .orElseThrow(() -> ExceptionResultEnum.FLOW_ENTITY_NO_DATA.exception());
            TFFlowApprove tfFlowApprove = tfFlowApproveService.findByFlowId(tfCustomFlowEntity.getFlowId());
            Optional.ofNullable(tfFlowApprove).orElseThrow(() -> ExceptionResultEnum.FLOW_APPROVE_NO_DATA.exception());

            if (tbCrmDetail.getStatus() == CrmStatusEnum.PUBLISH) {
                if (Objects.nonNull(tfFlowApprove.getStatus()) && (tfFlowApprove.getStatus() == FlowStatusEnum.FINISH
                        || tfFlowApprove.getStatus() == FlowStatusEnum.END)) {
                    throw ExceptionResultEnum.ERROR.exception("流程已结束，无法转审");
                }
            }
            TFCustomFlow tfCustomFlow = tfCustomFlowService.findById(tfCustomFlowEntity.gettFCustomFlowId());
            Optional.ofNullable(tfCustomFlow).orElseThrow(() -> ExceptionResultEnum.FLOW_CUSTOM_NO_DATA.exception());
            tbSopInfoService.updateFlowInfo(sopAllocationParam.getAllocationParams(), tfCustomFlow, tfCustomFlowEntity,
                    tfFlowApprove, tbCrmDetail);
        }
        return true;
    }

    /**
     * 查询sop状态
     *
     * @param sopNo
     * @return
     */
    @Override
    public SopStatusEnum findBySopNo(String sopNo) {
        TBSopInfo tbSopInfo = this.findByCode(sopNo);
        return tbSopInfo.getStatus();
    }

    /**
     * 查询sop信息
     *
     * @param sopNo
     * @return
     */
    @Override
    public TBSopInfo findByCode(String sopNo) {
        TBSopInfo tbSopInfo = this.getOne(new QueryWrapper<TBSopInfo>().lambda().eq(TBSopInfo::getSopNo, sopNo));
        Objects.requireNonNull(tbSopInfo, "未找到sop信息");
        return tbSopInfo;
    }

    /**
     * sop人员配置
     *
     * @param sopAllocationParam
     * @return
     */
    @Override
    @Transactional
    public Boolean sopAllocation(SopAllocationParam sopAllocationParam) throws InterruptedException {
        TFCustomFlowEntity tfCustomFlowEntity = tfCustomFlowEntityService.findByFlowId(sopAllocationParam.getFlowId());
        Optional.ofNullable(tfCustomFlowEntity).orElseThrow(() -> ExceptionResultEnum.FLOW_ENTITY_NO_DATA.exception());
        TBCrmDetail tbCrmDetail = tbCrmDetailService.findBySopNo(tfCustomFlowEntity.getCode());
        Objects.requireNonNull(tbCrmDetail, "未找到派单sop信息");
        TFFlowApprove tfFlowApprove = tfFlowApproveService.findByFlowId(tfCustomFlowEntity.getFlowId());
        Optional.ofNullable(tfFlowApprove).orElseThrow(() -> ExceptionResultEnum.FLOW_APPROVE_NO_DATA.exception());

        if (Objects.nonNull(tfFlowApprove.getStatus()) && (tfFlowApprove.getStatus() == FlowStatusEnum.FINISH
                || tfFlowApprove.getStatus() == FlowStatusEnum.END)) {
            throw ExceptionResultEnum.ERROR.exception("流程已结束，无法分配人员");
        }
        TFCustomFlow tfCustomFlow = tfCustomFlowService.findById(tfCustomFlowEntity.gettFCustomFlowId());
        Optional.ofNullable(tfCustomFlow).orElseThrow(() -> ExceptionResultEnum.FLOW_CUSTOM_NO_DATA.exception());
        tbSopInfoService.updateFlowInfo(sopAllocationParam.getAllocationParams(), tfCustomFlow, tfCustomFlowEntity,
                tfFlowApprove, tbCrmDetail);
        tbUserArchivesAllocationService.editCrmAllocation(new UserArchivesAllocationParam(sopAllocationParam));
        //        FlowResult flowResult = JSONObject.parseObject(tfCustomFlow.getFlowProcessVar(), FlowResult.class);
        //        FlowResult flowResultEntity = JSONObject.parseObject(tfCustomFlowEntity.getFlowProcessVar(), FlowResult.class);
        //        LinkedHashMap<String, FlowTaskResult> setupMap = flowResult.getSetupMap();
        //        LinkedHashMap<String, FlowTaskResult> setupEntityMap = flowResultEntity.getSetupMap();
        //        FlowTaskResult flowTaskResult = this.getFormProperties(tfCustomFlowEntity, tfFlowApprove.getSetup());
        //        FlowTaskResult currFlowTaskResult = GsonUtil.fromJson(
        //                GsonUtil.toJson(setupMap.get(flowTaskResult.getTaskKey())), FlowTaskResult.class);
        //        setupEntityMap.put(flowTaskResult.getTaskKey(), currFlowTaskResult);
        //        flowResultEntity.setSetupMap(setupEntityMap);
        //        tfCustomFlowEntity.setFlowProcessVar(JacksonUtil.parseJson(flowResultEntity));
        //        tfCustomFlowEntityService.updateById(tfCustomFlowEntity);
        tbSopInfoService.sopProxySystemApprove(tfCustomFlowEntity.getFlowId(), tfCustomFlowEntity.getCode());
        return true;
    }

    /**
     * 修改流程第一步
     *
     * @param flowId
     * @param crmDetailId
     * @param engineerUserIdList
     * @param regionCoordinatorUserId
     * @param projectManagerUserId
     * @return
     */
    @Override
    @Transactional
    public Boolean updateFlowSetupOne(Long flowId, Long crmDetailId, List<String> engineerUserIdList,
            String regionCoordinatorUserId, String projectManagerUserId) {
        boolean lock = memoryLock.lock(SystemConstant.LOCK_FLOW_PROPERTIES_PREFIX + crmDetailId, crmDetailId,
                SystemConstant.LOCK_FLOW_TIME_OUT);
        if (!lock) {
            throw ExceptionResultEnum.ERROR.exception("正在保存数据，请稍候再试！");
        }
        try {
            TFCustomFlowEntity tfCustomFlowEntity = tfCustomFlowEntityService.findByFlowId(flowId);
            FlowResult flowResult = JSONObject.parseObject(tfCustomFlowEntity.getFlowProcessVar(), FlowResult.class);
            LinkedHashMap<String, FlowTaskResult> setupMap = flowResult.getSetupMap();
            FlowTaskResult flowTaskResult = tbSopInfoService.getFormProperties(tfCustomFlowEntity, 1);
            if (flowTaskResult.getSetup() > 0) {
                List<FlowFormWidgetResult> flowFormWidgetResultList = flowTaskResult.getFormProperty();
                if (!CollectionUtils.isEmpty(engineerUserIdList)) {
                    for (FlowFormWidgetResult f : flowFormWidgetResultList) {
                        if (f.getFormId().contains(ProcessLimitedEnum.ENGINEER_USERS_ID.getKey())) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE, engineerUserIdList);
                            f.setValue(jsonObject.toJSONString());
                        } else if (f.getFormId().contains(ProcessLimitedEnum.PROJECT_MANAGER_ID.getKey())) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE, projectManagerUserId);
                            f.setValue(jsonObject.toJSONString());
                        } else if (f.getFormId().contains(ProcessLimitedEnum.REGION_COORDINATOR_ID.getKey())) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE, regionCoordinatorUserId);
                            f.setValue(jsonObject.toJSONString());
                        }
                    }
                } else {
                    CrmProjectResult crmProjectResult = tbCrmService.findCrmProjectInfo(tfCustomFlowEntity.getCode(),
                            crmDetailId, tfCustomFlowEntity.getCrmNo());
                    Optional.ofNullable(crmProjectResult)
                            .orElseThrow(() -> ExceptionResultEnum.CRM_NO_NO_DATA.exception());
                    for (FlowFormWidgetResult f : flowFormWidgetResultList) {
                        if (f.getFormId().contains(ProcessLimitedEnum.ENGINEER_USERS_ID.getKey())) {
                            List<String> list = new ArrayList<>(crmProjectResult.getEngineerList().size());
                            crmProjectResult.getEngineerList().stream().peek(s -> list.add(s.getUserId().toString()))
                                    .collect(Collectors.toList());
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE, list);
                            f.setValue(jsonObject.toJSONString());
                        } else if (f.getFormId().contains(ProcessLimitedEnum.PROJECT_MANAGER_ID.getKey())) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE,
                                    crmProjectResult.getProjectManagerList().get(0).getUserId().toString());
                            f.setValue(jsonObject.toJSONString());
                        }
                        if (f.getFormId().contains(ProcessLimitedEnum.REGION_COORDINATOR_ID.getKey())) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put(SystemConstant.VALUE,
                                    !CollectionUtils.isEmpty(crmProjectResult.getRegionCoordinatorList()) ?
                                            crmProjectResult.getRegionCoordinatorList().get(0).getUserId().toString() :
                                            null);
                            f.setValue(jsonObject.toJSONString());
                        }
                    }
                }
            }
            setupMap.put(flowTaskResult.getTaskKey(), flowTaskResult);
            tfCustomFlowEntity.setFlowProcessVar(JacksonUtil.parseJson(flowResult));
            return tfCustomFlowEntityService.updateById(tfCustomFlowEntity);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        } finally {
            memoryLock.unlock(SystemConstant.LOCK_FLOW_PROPERTIES_PREFIX + crmDetailId);
        }
        return false;
    }

    /**
     * 更新流程信息
     *
     * @param allocationParamList
     * @param tfCustomFlow
     * @param tfCustomFlowEntity
     * @param tfFlowApprove
     * @param tbCrmDetail
     * @return
     * @throws InterruptedException
     */
    @Override
    @Transactional
    public Boolean updateFlowInfo(List<AllocationParam> allocationParamList, TFCustomFlow tfCustomFlow,
            TFCustomFlowEntity tfCustomFlowEntity, TFFlowApprove tfFlowApprove, TBCrmDetail tbCrmDetail)
            throws InterruptedException {
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(tfCustomFlowEntity.getFlowId().toString())
                .list();
        if (CollectionUtils.isEmpty(taskList)) {
            throw ExceptionResultEnum.FLOW_TASK_NO_DATA.exception();
        }
        if (!CollectionUtils.isEmpty(allocationParamList)) {
            List<String> engineerUserIdList = new ArrayList<>();
            String regionCoordinatorUserId = null, projectManagerUserId = null;
            for (AllocationParam a : allocationParamList) {
                if (a.getSopRoleType() == SopRoleTypeEnum.PROJECT_MANAGER) {
                    //修改流程里面的工程师
                    projectManagerUserId = a.getUserIdList().get(0).toString();
                    if (taskList.size() == 1) {
                        activitiService.taskApproverExchange(a.getUserIdList().get(0),
                                Long.parseLong(taskList.get(0).getId()));
                    } else {
                        CrmProjectResult crmProjectResult = tbCrmService.findCrmProjectInfo(
                                tfCustomFlowEntity.getCode(), tbCrmDetail.getId(), tfCustomFlowEntity.getCrmNo());
                        List<Task> taskTempList = taskList.stream().filter(s -> Objects.equals(s.getAssignee(),
                                        crmProjectResult.getProjectManagerList().get(0).getUserId().toString()))
                                .collect(Collectors.toList());
                        if (CollectionUtils.isEmpty(taskTempList)) {
                            throw ExceptionResultEnum.FLOW_TASK_NO_DATA.exception();
                        }
                        activitiService.taskApproverExchange(a.getUserIdList().get(0),
                                Long.parseLong(taskTempList.get(0).getId()));
                    }
                } else if (a.getSopRoleType() == SopRoleTypeEnum.ENGINEER) {
                    //修改流程里面的工程师
                    engineerUserIdList = a.getUserIdList().stream().map(s -> s.toString()).collect(Collectors.toList());
                } else if (a.getSopRoleType() == SopRoleTypeEnum.REGION_COORDINATOR) {
                    //修改流程里面的区域协调人
                    if (!CollectionUtils.isEmpty(a.getUserIdList())) {
                        regionCoordinatorUserId = a.getUserIdList().get(0).toString();
                    }
                }
            }
            tbSopInfoService.updateFlowSetupOne(tfCustomFlowEntity.getFlowId(), tbCrmDetail.getId(), engineerUserIdList,
                    regionCoordinatorUserId, projectManagerUserId);
        }
        return true;
    }

    /**
     * sop填报信息
     *
     * @param flowApproveParam
     * @return
     */
    @Override
    @Transactional
    public Boolean sopApprove(FlowApproveParam flowApproveParam) throws InterruptedException {
        Optional.ofNullable(flowApproveParam.getTaskId())
                .orElseThrow(() -> ExceptionResultEnum.FLOW_TASK_ID_IS_NULL.exception());
        if (flowApproveParam.getApprove() != FlowApprovePassEnum.PASS
                && flowApproveParam.getApprove() != FlowApprovePassEnum.REJECT
                && flowApproveParam.getApprove() != FlowApprovePassEnum.DRAFT
                && flowApproveParam.getApprove() != FlowApprovePassEnum.START) {
            throw ExceptionResultEnum.PARAMS_ERROR.exception("sop填报只能为通过或驳回或草稿或提交");
        }
        Optional.ofNullable(flowApproveParam.getFormProperties())
                .orElseThrow(() -> ExceptionResultEnum.FLOW_FORM_PROPERTIES_IS_NULL.exception());
        if (flowApproveParam.getApprove() == FlowApprovePassEnum.REJECT && Objects.isNull(
                flowApproveParam.getSetup())) {
            throw ExceptionResultEnum.FLOW_REJECT_SETUP_IS_NULL.exception();
        }
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        Task task = taskService.createTaskQuery().taskId(String.valueOf(flowApproveParam.getTaskId())).singleResult();
        Optional.ofNullable(task).orElseThrow(() -> ExceptionResultEnum.FLOW_TASK_NO_DATA.exception());

        List<String> taskApproveUserList = activitiService.getTaskApprove(task);
        if (flowApproveParam.getApprove() != FlowApprovePassEnum.DRAFT && !CollectionUtils.isEmpty(taskApproveUserList)
                && !taskApproveUserList.contains(sysUser.getId().toString())) {
            throw ExceptionResultEnum.FLOW_SOP_DATA_NOT_ME.exception();
        }

        TFCustomFlowEntity tfCustomFlowEntity = tfCustomFlowEntityService.findByFlowId(
                Long.parseLong(task.getProcessInstanceId()));
        TFCustomFlow tfCustomFlow = tfCustomFlowService.findById(tfCustomFlowEntity.gettFCustomFlowId());

        if (tfCustomFlow.getType() != TFCustomTypeEnum.OFFICE_SOP_FLOW
                && tfCustomFlow.getType() != TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW) {
            throw ExceptionResultEnum.ERROR.exception("sop流程类型错误");
        }

        TBSopInfo tbSopInfo = this.getOne(
                new QueryWrapper<TBSopInfo>().lambda().eq(TBSopInfo::getSopNo, tfCustomFlowEntity.getCode()));
        Optional.ofNullable(tbSopInfo).orElseThrow(() -> ExceptionResultEnum.SOP_INFO_NO_DATA.exception());

        TBCrmDetail tbCrmDetail = tbCrmDetailService.findBySopNo(tfCustomFlowEntity.getCode());
        Objects.requireNonNull(tbCrmDetail, "未找到派单sop信息");
        CrmProjectResult crmProjectResult = tbCrmService.findCrmProjectInfo(tfCustomFlowEntity.getCode(),
                tbCrmDetail.getId(), tfCustomFlowEntity.getCrmNo());
        Optional.ofNullable(crmProjectResult).orElseThrow(() -> ExceptionResultEnum.CRM_NO_NO_DATA.exception());

        FlowTaskResult flowTaskResult = JSONObject.parseObject(flowApproveParam.getFormProperties(),
                FlowTaskResult.class);
        if (flowApproveParam.getApprove() == FlowApprovePassEnum.PASS
                || flowApproveParam.getApprove() == FlowApprovePassEnum.START) {
            FlowTaskResult flowTaskResultOne = this.getFormProperties(tfCustomFlowEntity, 1);
            FlowTaskResult currFlowTaskResult = JSONObject.parseObject(flowApproveParam.getFormProperties(),
                    FlowTaskResult.class);
            List<String> approveUserIds = this.getNextApproveUserIds(tfCustomFlow.getType(), flowTaskResultOne,
                    currFlowTaskResult, crmProjectResult, tbCrmDetail.getId());
            flowApproveParam.setApproveUserIds(approveUserIds);
        }

        if (tfCustomFlow.getType() == TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW) {
            if (flowTaskResult.getSetup().intValue() == 2) {
                List<FlowFormWidgetResult> flowFormWidgetResultList = flowTaskResult.getFormProperty();
                for (FlowFormWidgetResult f : flowFormWidgetResultList) {
                    if (f.getFormId().contains(ProcessLimitedEnum.SCAN_START_TIME.getKey()) && Objects.nonNull(
                            f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                        JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                        String value = jsonObject.getString(SystemConstant.VALUE);
                        tbCrmDetail.setScanStartTime(Long.parseLong(value));
                    } else if (f.getFormId().contains(ProcessLimitedEnum.SCAN_END_TIME.getKey()) && Objects.nonNull(
                            f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                        JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                        String value = jsonObject.getString(SystemConstant.VALUE);
                        tbCrmDetail.setScanEndTime(Long.parseLong(value));
                        if (Objects.nonNull(tbCrmDetail.getScanStartTime())
                                && tbCrmDetail.getScanEndTime().longValue() <= tbCrmDetail.getScanStartTime()
                                .longValue()) {
                            throw ExceptionResultEnum.ERROR.exception("扫描结束时间不能小于等于扫描开始时间");
                        }
                    } else if (f.getFormId().contains(ProcessLimitedEnum.MARK_START_TIME.getKey()) && Objects.nonNull(
                            f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                        JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                        String value = jsonObject.getString(SystemConstant.VALUE);
                        tbCrmDetail.setMarkPaperStartTime(Long.parseLong(value));
                        if (Objects.nonNull(tbCrmDetail.getScanEndTime())
                                && tbCrmDetail.getMarkPaperStartTime().longValue() <= tbCrmDetail.getScanEndTime()
                                .longValue()) {
                            throw ExceptionResultEnum.ERROR.exception("阅卷开始时间不能小于等于扫描结束时间");
                        }
                    } else if (f.getFormId().contains(ProcessLimitedEnum.MARK_END_TIME.getKey()) && Objects.nonNull(
                            f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                        JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                        String value = jsonObject.getString(SystemConstant.VALUE);
                        tbCrmDetail.setMarkPaperEndTime(Long.parseLong(value));
                        if (Objects.nonNull(tbCrmDetail.getMarkPaperStartTime())
                                && tbCrmDetail.getMarkPaperEndTime().longValue() <= tbCrmDetail.getMarkPaperStartTime()
                                .longValue()) {
                            throw ExceptionResultEnum.ERROR.exception("阅卷结束时间不能小于等于阅卷开始时间");
                        }
                    }
                    tbCrmDetail.updateInfo(sysUser.getId());
                    tbCrmDetailService.updateById(tbCrmDetail);
                }
            } else if (flowTaskResult.getSetup().intValue() == 3) {//内审时要set全部值
                FlowResult flowResult = JSONObject.parseObject(tfCustomFlowEntity.getFlowProcessVar(),
                        FlowResult.class);
                FlowTaskResult currFlowTaskResult = GsonUtil.fromJson(
                        GsonUtil.toJson(flowResult.getSetupMap().get(flowTaskResult.getTaskKey())),
                        FlowTaskResult.class);
                List<FlowFormWidgetResult> flowFormWidgetResultList = currFlowTaskResult.getFormProperty();
                Map<String, FlowFormWidgetResult> flowTaskResultMap = flowTaskResult.getFormProperty().stream().collect(
                        Collectors.toMap(FlowFormWidgetResult::getFormId, Function.identity(), (dto1, dto2) -> dto1));
                for (FlowFormWidgetResult f : flowFormWidgetResultList) {
                    if (flowTaskResultMap.containsKey(f.getFormId())) {
                        f.setValue(flowTaskResultMap.get(f.getFormId()).getValue());
                    }
                }
                flowTaskResult.setFormProperty(flowFormWidgetResultList);
                flowApproveParam.setFormProperties(JacksonUtil.parseJson(flowTaskResult));
            } else if (flowTaskResult.getSetup().intValue() == 10
                    && flowApproveParam.getApprove() == FlowApprovePassEnum.PASS) {
                //设备签收登记列表查询
                List<DeviceSignResult> deviceSignResultList = tbDeviceDeliveryService.signList(
                        tfCustomFlowEntity.getCrmNo());
                if (!CollectionUtils.isEmpty(deviceSignResultList)) {
                    List<DeviceSignResult> tempList = deviceSignResultList.stream()
                            .filter(s -> s.getStatus() == DeviceDeliveryStatusEnum.USING).collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(tempList)) {
                        throw ExceptionResultEnum.ERROR.exception("有设备在使用中，请处理后再提交！");
                    }
                }
                //设备去处登记列表查询
                List<DevicePlaceResult> devicePlaceResultList = tbDeviceDeliveryService.placeList(
                        tfCustomFlowEntity.getCrmNo());
                if (!CollectionUtils.isEmpty(devicePlaceResultList)) {
                    List<DevicePlaceResult> tempList = devicePlaceResultList.stream()
                            .filter(s -> s.getStatus() == DeviceDeliveryStatusEnum.USING).collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(tempList)) {
                        throw ExceptionResultEnum.ERROR.exception("有设备在使用中，请处理后再提交！");
                    }
                }
            }
        }

        String oldFlowProcessVar = tfCustomFlowEntity.getFlowProcessVar();
        Map<String, Object> map = activitiService.taskApprove(flowApproveParam);
        if (flowApproveParam.getApprove() != FlowApprovePassEnum.DRAFT) {
            map.put(SystemConstant.CRM_INFO, crmProjectResult);
            map.put(SystemConstant.SOP_ID, tbSopInfo.getId());
            tbSopInfoService.saveJobRemind(map);

            TFFlowApprove tfFlowApprove = (TFFlowApprove) map.get(SystemConstant.FLOW_APPROVE);
            Optional.ofNullable(tfFlowApprove).orElseThrow(() -> ExceptionResultEnum.FLOW_APPROVE_NO_DATA.exception());

            //新增保存实际时间
            tbCrmDetailService.saveActualTime(tfCustomFlow.getType(), tfFlowApprove, tbCrmDetail);

            //新增sop日志
            TBSopLog tbSopLog = new TBSopLog(tbSopInfo.getId(), SopLogTypeEnum.APPROVE, tfFlowApprove.getStatus(),
                    tfFlowApprove.getSetup(), oldFlowProcessVar, tfCustomFlowEntity.getFlowProcessVar(),
                    sysUser.getId());
            tbSopLogService.save(tbSopLog);
        }
        TFFlowApprove tfFlowApprove = (TFFlowApprove) map.get(SystemConstant.FLOW_APPROVE);
        tbSopInfo.setStatus(
                tfFlowApprove.getSetup().intValue() == 1 && tfFlowApprove.getStatus() == FlowStatusEnum.DRAFT ?
                        SopStatusEnum.DRAFT :
                        SopStatusEnum.START);
        tbSopInfo.updateInfo(sysUser.getId());
        tbSopInfoService.updateById(tbSopInfo);

        //研究生流程，如果下一步审批是大区经理内审并且项目经理和大区经理为同一人时，后台自动审批
        tbSopInfoService.sopSystemApprove(tfCustomFlow, tfCustomFlowEntity, tfFlowApprove, crmProjectResult,
                flowApproveParam.getCrmNo(), tbCrmDetail.getId());
        return true;
    }

    /**
     * sop自动审批
     *
     * @param tfCustomFlow
     * @param tfCustomFlowEntity
     * @param tfFlowApprove
     * @param crmProjectResult
     * @param crmNo
     * @param crmDetailId
     * @throws InterruptedException
     */
    @Override
    @Transactional
    public void sopSystemApprove(TFCustomFlow tfCustomFlow, TFCustomFlowEntity tfCustomFlowEntity,
            TFFlowApprove tfFlowApprove, CrmProjectResult crmProjectResult, String crmNo, Long crmDetailId)
            throws InterruptedException {
        //如果下一步审批是内审并且项目经理和大区经理为同一人时，后台自动审批
        if (tfCustomFlow.getType() == TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW
                && tfFlowApprove.getStatus() != FlowStatusEnum.FINISH && tfFlowApprove.getStatus() != FlowStatusEnum.END
                && tfFlowApprove.getStatus() == FlowStatusEnum.AUDITING && tfFlowApprove.getSetup().intValue() == 3
                && crmProjectResult.getRegionManagerId().longValue() == crmProjectResult.getProjectManagerList().get(0)
                .getUserId().longValue()) {
            List<Task> taskList = taskService.createTaskQuery()
                    .processInstanceId(String.valueOf(tfFlowApprove.getFlowId()))
                    .taskAssignee(String.valueOf(crmProjectResult.getRegionManagerId())).list();
            if (!CollectionUtils.isEmpty(taskList) && taskList.size() == 2) {
                Task taskNew = taskList.get(0);
                FlowResult flowResult = JSONObject.parseObject(tfCustomFlowEntity.getFlowProcessVar(),
                        FlowResult.class);
                LinkedHashMap<String, FlowTaskResult> setupMap = flowResult.getSetupMap();
                FlowTaskResult nextFlowTaskResult = null;
                for (Map.Entry<String, FlowTaskResult> entry : setupMap.entrySet()) {
                    if (entry.getValue().getSetup().intValue() == 3) {
                        nextFlowTaskResult = entry.getValue();
                        break;
                    }
                }

                if (Objects.nonNull(nextFlowTaskResult)) {
                    List<FlowFormWidgetResult> flowFormWidgetResultList = nextFlowTaskResult.getFormProperty();
                    for (FlowFormWidgetResult f : flowFormWidgetResultList) {
                        if (f.getFormId().contains(SystemConstant.APPROVE_RADIO_REGION)) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put("value", "1");
                            f.setValue(jsonObject.toString());
                        } else if (f.getFormId().contains(SystemConstant.APPROVE_REMARK_REGION)) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put("value", SystemConstant.SYSTEM_AUTO_APPROVE);
                            f.setValue(jsonObject.toString());
                        } else if (f.getFormId().contains(SystemConstant.SIGN_REGION)) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put("value", "https://sop-file.qmth.com.cn/logo/logo.png");
                            f.setValue(jsonObject.toString());
                        }
                    }
                }
                activitiService.taskApprove(
                        new FlowApproveParam(Long.parseLong(taskNew.getId()), FlowApprovePassEnum.PASS,
                                this.getNextApproveUserIds(tfCustomFlow.getType(), nextFlowTaskResult,
                                        nextFlowTaskResult, crmProjectResult, crmDetailId), crmNo,
                                SystemConstant.SYSTEM_AUTO_APPROVE, JacksonUtil.parseJson(nextFlowTaskResult),
                                Collections.singletonMap(SystemConstant.APPROVE_AUTO, SystemConstant.APPROVE_AUTO)));
            }
        }
    }

    /**
     * 代理自动审批
     *
     * @param flowId
     * @param sopNo
     */
    @Override
    @Transactional
    public void sopProxySystemApprove(Long flowId, String sopNo) throws InterruptedException {
        TFCustomFlowEntity tfCustomFlowEntity = Objects.nonNull(flowId) ?
                tfCustomFlowEntityService.findByFlowId(flowId) :
                tfCustomFlowEntityService.findByCode(sopNo);
        Optional.ofNullable(tfCustomFlowEntity).orElseThrow(() -> ExceptionResultEnum.FLOW_ENTITY_NO_DATA.exception());
        TFCustomFlow tfCustomFlow = tfCustomFlowService.findById(tfCustomFlowEntity.gettFCustomFlowId());
        Optional.ofNullable(tfCustomFlow).orElseThrow(() -> ExceptionResultEnum.FLOW_CUSTOM_NO_DATA.exception());

        if (tfCustomFlow.getType() == TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW) {
            TBCrmDetail tbCrmDetail = tbCrmDetailService.findBySopNo(tfCustomFlowEntity.getCode());
            Objects.requireNonNull(tbCrmDetail, "未找到派单sop信息");
            TFFlowApprove tfFlowApprove = tfFlowApproveService.findByFlowId(tfCustomFlowEntity.getFlowId());
            Optional.ofNullable(tfFlowApprove).orElseThrow(() -> ExceptionResultEnum.FLOW_APPROVE_NO_DATA.exception());

            if (Objects.nonNull(tfFlowApprove.getStatus()) && (tfFlowApprove.getStatus() == FlowStatusEnum.FINISH
                    || tfFlowApprove.getStatus() == FlowStatusEnum.END)) {
                throw ExceptionResultEnum.ERROR.exception("流程已结束，无法自动审批");
            }
            CrmProjectResult crmProjectResult = tbCrmService.findCrmProjectInfo(tfCustomFlowEntity.getCode(),
                    tbCrmDetail.getId(), tfCustomFlowEntity.getCrmNo());
            tbSopInfoService.sopSystemApprove(tfCustomFlow, tfCustomFlowEntity, tfFlowApprove, crmProjectResult,
                    tbCrmDetail.getCrmNo(), tbCrmDetail.getId());
        }
    }

    /**
     * sop管理列表
     *
     * @param sopInfoListParam
     * @return
     */
    @Override
    public IPage<Map> list(SopInfoListParam sopInfoListParam) {
        if (sopInfoListParam.getType() != TFCustomTypeEnum.OFFICE_SOP_FLOW
                && sopInfoListParam.getType() != TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW) {
            throw ExceptionResultEnum.ERROR.exception("流程类型只能为教务处或研究生");
        }
        TFCustomFlow maxTfCustomFlow = tfCustomFlowService.findMaxVersion(null, null, sopInfoListParam.getType());
        Optional.ofNullable(maxTfCustomFlow).orElseThrow(() -> ExceptionResultEnum.FLOW_CUSTOM_NO_DATA.exception());
        sopInfoListParam.setVersion(maxTfCustomFlow.getVersion());
        String tableName = sopInfoListParam.getType().getTableName() + "_" + sopInfoListParam.getVersion();
        String fieldName = null, fieldValue = null, fieldOrder = null, defaultFieldName = null, defaultFieldOrder = null;
        StringJoiner stringJoinerView = new StringJoiner(",");
        StringJoiner stringJoinerCondition = new StringJoiner("");
        StringJoiner stringJoinerOrder = new StringJoiner("");
        StringJoiner stringJoinerDefaultView = new StringJoiner(",");
        StringJoiner stringJoinerDefaultFieldOrderView = new StringJoiner(",");
        stringJoinerDefaultFieldOrderView = stringJoinerDefaultFieldOrderView.add("order by tbsi.update_time desc");

        //显示字段
        boolean pendApproveName = false;
        if (!CollectionUtils.isEmpty(sopInfoListParam.getFormWidgetMetadataViewList())) {
            for (FormWidgetMetadataParam f : sopInfoListParam.getFormWidgetMetadataViewList()) {
                //加入固定字段转换
                if (SystemConstant.SOP_TRANSFORM_MAP.containsKey(f.getFieldId())) {
                    if (Objects.equals(f.getFieldId(), "pendApproveName")) {
                        pendApproveName = true;
                    } else if (Objects.equals(f.getFieldId(), "taskName")) {
                        stringJoinerDefaultView.add(
                                "IFNULL(" + SystemConstant.SOP_TRANSFORM_MAP.get(f.getFieldId()) + ",'已完成') as "
                                        + f.getFieldId());
                    } else if (Objects.equals(f.getFieldId(), "sopNo")) {
                        stringJoinerDefaultFieldOrderView.add(
                                SystemConstant.SOP_TRANSFORM_MAP.get(f.getFieldId()) + " desc");
                        stringJoinerDefaultView.add(
                                SystemConstant.SOP_TRANSFORM_MAP.get(f.getFieldId()) + " as " + f.getFieldId());
                    } else {
                        stringJoinerDefaultView.add(
                                SystemConstant.SOP_TRANSFORM_MAP.get(f.getFieldId()) + " as " + f.getFieldId());
                    }
                } else {
                    stringJoinerView.add(f.getFieldId());
                }
            }
            fieldName = stringJoinerView.toString();
        }
        stringJoinerDefaultView.add(SystemConstant.SOP_TRANSFORM_MAP.get("status") + " as status");
        defaultFieldName = stringJoinerDefaultView.toString();
        defaultFieldOrder = stringJoinerDefaultFieldOrderView.toString();

        //筛选字段
        if (!CollectionUtils.isEmpty(sopInfoListParam.getFormWidgetMetadataConditionList())) {
            LinkedMultiValueMap<String, FormWidgetMetadataParam> formWidgetMetadataParamLinkedMultiValueMap = new LinkedMultiValueMap<>(
                    sopInfoListParam.getFormWidgetMetadataConditionList().size());
            for (FormWidgetMetadataParam f : sopInfoListParam.getFormWidgetMetadataConditionList()) {
                if (Objects.nonNull(f.getOperator())) {
                    //加入固定字段转换
                    if (SystemConstant.SOP_TRANSFORM_MAP.containsKey(f.getFieldId())) {
                        if (Objects.equals(f.getFieldId().toLowerCase(), "status")) {
                            JSONArray jsonArray = JSONArray.parseArray(f.getFieldValue());
                            if (Objects.nonNull(jsonArray) && jsonArray.size() > 0) {
                                StringJoiner stringJoiner = new StringJoiner("").add("(");
                                f.setOperator(f.getOperator() == FormWidgetMetadataOperator.EQ ?
                                        FormWidgetMetadataOperator.IN :
                                        FormWidgetMetadataOperator.NOT_IN);
                                for (int i = 0; i < jsonArray.size(); i++) {
                                    if (Objects.equals(jsonArray.getString(i), FlowStatusEnum.AUDITING.name())) {
                                        stringJoiner.add(
                                                "'" + FlowStatusEnum.START.name() + "','" + FlowStatusEnum.DRAFT.name()
                                                        + "','" + FlowStatusEnum.AUDITING.name() + "','"
                                                        + FlowStatusEnum.REJECT.name() + "','"
                                                        + FlowStatusEnum.CANCEL.name() + "',");
                                    } else if (Objects.equals(jsonArray.getString(i), FlowStatusEnum.FINISH.name())) {
                                        stringJoiner.add("'" + FlowStatusEnum.FINISH.name() + "',");
                                    }
                                }
                                String s = stringJoiner.toString().substring(0, stringJoiner.length() - 1) + ")";
                                f.setFieldValue(s);
                            }
                        }
                        String fieldId = SystemConstant.SOP_TRANSFORM_MAP.get(f.getFieldId());
                        f.setFieldId(fieldId);
                        formWidgetMetadataParamLinkedMultiValueMap.add(fieldId, f);
                    } else {
                        formWidgetMetadataParamLinkedMultiValueMap.add(f.getFieldId(), f);
                    }
                }
            }
            for (Map.Entry<String, List<FormWidgetMetadataParam>> entry : formWidgetMetadataParamLinkedMultiValueMap.entrySet()) {
                List<FormWidgetMetadataParam> formWidgetMetadataParamList = entry.getValue();
                if (Objects.isNull(sopInfoListParam.getFormWidgetMetadataOperation())) {
                    sopInfoListParam.setFormWidgetMetadataOperation(FormWidgetMetadataOperation.AND);
                }
                if (formWidgetMetadataParamList.size() > 1) {
                    StringJoiner conditionJoin = new StringJoiner("").add(
                            sopInfoListParam.getFormWidgetMetadataOperation().getCode()).add(" ( ");
                    for (int y = 0; y < formWidgetMetadataParamList.size(); y++) {
                        FormWidgetMetadataParam f = formWidgetMetadataParamList.get(y);
                        if (f.getOperator() == FormWidgetMetadataOperator.LIKE) {
                            conditionJoin.add(f.getFieldId()).add(f.getOperator().getCode()).add("('%','")
                                    .add(f.getFieldValue()).add("','%')");
                        } else if (f.getOperator() == FormWidgetMetadataOperator.RANGE) {
                            if (f.getFieldValue().startsWith("[")) {
                                String[] fieldValueCovers = f.getFieldValue().replaceAll("\\[", "")
                                        .replaceAll("\\]", "").replaceAll(", ", ",").split(",");
                                String[] operators = FormWidgetMetadataOperator.RANGE.getCode().split(",");
                                for (int i = 0; i < fieldValueCovers.length; i++) {
                                    conditionJoin.add(sopInfoListParam.getFormWidgetMetadataOperation().getCode())
                                            .add(f.getFieldId()).add(operators[i]).add(fieldValueCovers[i]);
                                }
                            }
                        } else if (f.getOperator() == FormWidgetMetadataOperator.IN
                                || f.getOperator() == FormWidgetMetadataOperator.NOT_IN) {
                            conditionJoin.add(f.getFieldId()).add(f.getOperator().getCode()).add(f.getFieldValue());
                        } else {
                            if (f.getFieldValue().startsWith("[")) {
                                String fieldValueCover = f.getFieldValue().replaceAll("\\[", "").replaceAll("\\]", "")
                                        .replaceAll(", ", ",");
                                conditionJoin.add(sopInfoListParam.getFormWidgetMetadataOperation().getCode())
                                        .add(f.getFieldId()).add(f.getOperator() == FormWidgetMetadataOperator.EQ ?
                                                FormWidgetMetadataOperator.IN.getCode() :
                                                FormWidgetMetadataOperator.NOT_IN.getCode()).add("(").add(fieldValueCover)
                                        .add(")").add("\r\n");
                            } else {
                                conditionJoin.add(f.getFieldId()).add(f.getOperator().getCode()).add("'")
                                        .add(f.getFieldValue()).add("'");
                            }
                        }
                        if (y < formWidgetMetadataParamList.size() - 1) {
                            conditionJoin.add(FormWidgetMetadataOperation.OR.getCode());
                        }
                    }
                    conditionJoin.add(" ) ");
                    stringJoinerCondition.add(conditionJoin.toString()).add("\r\n");
                } else {
                    for (FormWidgetMetadataParam f : formWidgetMetadataParamList) {
                        if (f.getOperator() == FormWidgetMetadataOperator.LIKE) {
                            stringJoinerCondition.add(sopInfoListParam.getFormWidgetMetadataOperation().getCode())
                                    .add(f.getFieldId()).add(f.getOperator().getCode()).add("('%','")
                                    .add(f.getFieldValue()).add("','%')").add("\r\n");
                        } else if (f.getOperator() == FormWidgetMetadataOperator.RANGE) {
                            if (f.getFieldValue().startsWith("[")) {
                                String[] fieldValueCovers = f.getFieldValue().replaceAll("\\[", "")
                                        .replaceAll("\\]", "").replaceAll(", ", ",").split(",");
                                String[] operators = FormWidgetMetadataOperator.RANGE.getCode().split(",");
                                for (int i = 0; i < fieldValueCovers.length; i++) {
                                    stringJoinerCondition.add(
                                                    sopInfoListParam.getFormWidgetMetadataOperation().getCode())
                                            .add(f.getFieldId()).add(operators[i]).add(fieldValueCovers[i]);
                                }
                            }
                        } else if (f.getOperator() == FormWidgetMetadataOperator.IN
                                || f.getOperator() == FormWidgetMetadataOperator.NOT_IN) {
                            stringJoinerCondition.add(sopInfoListParam.getFormWidgetMetadataOperation().getCode())
                                    .add(f.getFieldId()).add(f.getOperator().getCode()).add(f.getFieldValue())
                                    .add("\r\n");
                        } else {
                            if (f.getFieldValue().startsWith("[")) {
                                String fieldValueCover = f.getFieldValue().replaceAll("\\[", "").replaceAll("\\]", "")
                                        .replaceAll(", ", ",");
                                stringJoinerCondition.add(sopInfoListParam.getFormWidgetMetadataOperation().getCode())
                                        .add(f.getFieldId()).add(f.getOperator() == FormWidgetMetadataOperator.EQ ?
                                                FormWidgetMetadataOperator.IN.getCode() :
                                                FormWidgetMetadataOperator.NOT_IN.getCode()).add("(").add(fieldValueCover)
                                        .add(")").add("\r\n");
                            } else {
                                stringJoinerCondition.add(sopInfoListParam.getFormWidgetMetadataOperation().getCode())
                                        .add(f.getFieldId()).add(f.getOperator().getCode()).add("'")
                                        .add(f.getFieldValue()).add("'").add("\r\n");
                            }
                        }
                    }
                }
            }
        }
        fieldValue = !Objects.equals(stringJoinerCondition.toString().trim(), "") ?
                stringJoinerCondition.toString().trim() :
                null;

        //排序字段
        if (!CollectionUtils.isEmpty(sopInfoListParam.getFormWidgetMetadataOrderList())) {
            for (FormWidgetMetadataParam f : sopInfoListParam.getFormWidgetMetadataOrderList()) {
                stringJoinerOrder.add(f.getFieldId()).add(f.getOrder().getCode()).add(",");
            }
            fieldOrder = stringJoinerOrder.toString();
            fieldOrder = fieldOrder.substring(0, fieldOrder.length() - 1);
        }

        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionDto dpr = sysUserService.buildUserDataPermission(requestUser.getId());
        IPage<Map> list = this.baseMapper.list(
                new Page<>(sopInfoListParam.getPageNumber(), sopInfoListParam.getPageSize()),
                Objects.nonNull(sopInfoListParam.getType()) ? sopInfoListParam.getType().name() : null,
                sopInfoListParam.getServiceId(), tableName, fieldName, fieldValue, dpr, fieldOrder, defaultFieldName,
                defaultFieldOrder, sopInfoListParam.getLeadId(), sopInfoListParam.getCrmUserId(),
                sopInfoListParam.getCustomName(), sopInfoListParam.getTaskKey(), sopInfoListParam.getCrmName(),
                sopInfoListParam.getSopNo(),
                Objects.nonNull(sopInfoListParam.getDingPlan()) ? sopInfoListParam.getDingPlan().toString() : null);
        for (Map m : list.getRecords()) {
            String engineerUsersId = (String) m.get("engineer_users_id_1");
            List<Long> userIdList = new ArrayList<>();
            Map<Long, String> map = new HashMap<>();
            Map<Long, String> engineerMap = new HashMap<>();//工程师map
            if (Objects.nonNull(engineerUsersId)) {
                if (engineerUsersId.contains(SystemConstant.LIST_JOIN_SPLIT)) {
                    String[] strs = engineerUsersId.split(",");
                    for (int i = 0; i < strs.length; i++) {
                        userIdList.add(Long.parseLong(strs[i]));
                        engineerMap.put(Long.parseLong(strs[i]), "engineer_users_id_1");
                    }
                } else {
                    userIdList.add(Long.parseLong(engineerUsersId));
                    engineerMap.put(Long.parseLong(engineerUsersId), "engineer_users_id_1");
                }
            }
            if (!CollectionUtils.isEmpty(userIdList)) {
                List<OrgUserNameDto> orgUserNameDtoList = sysUserService.findOrgUserName(userIdList);
                StringJoiner stringJoiner = new StringJoiner(SystemConstant.LIST_JOIN_SPLIT);
                for (OrgUserNameDto s : orgUserNameDtoList) {
                    if (map.containsKey(s.getId())) {
                        m.put(map.get(s.getId()), s.getOrgUserName());
                    }
                    if (engineerMap.containsKey(s.getId())) {
                        stringJoiner.add(s.getOrgUserName());
                    }
                }
                if (!CollectionUtils.isEmpty(engineerMap)) {
                    m.put("engineer_users_id_1", stringJoiner.toString());
                }
            }

            String flowId = (String) m.get("flowId");
            if (pendApproveName) {
                TFFlowLog tfFlowLog = tfFlowLogService.findByLastFlowLog(Long.parseLong(flowId));
                if (Objects.nonNull(tfFlowLog) && Objects.nonNull(tfFlowLog.getPendApproveId())) {
                    List<String> userList = Arrays.asList(
                            tfFlowLog.getPendApproveId().split(SystemConstant.LIST_JOIN_SPLIT));
                    List<OrgUserNameDto> orgUserNameDtoList = sysUserService.findOrgUserName(
                            userList.stream().map(s -> Long.parseLong(s)).collect(Collectors.toList()));
                    List<String> userName = orgUserNameDtoList.stream().map(s -> s.getOrgUserName())
                            .collect(Collectors.toList());
                    m.put("pendApproveName", StringUtils.join(userName, SystemConstant.LIST_JOIN_SPLIT));
                } else {
                    m.put("pendApproveName", "--");
                }
            }

            List<Task> taskList = taskService.createTaskQuery().processInstanceId(flowId).list();
            m.put("myself", false);
            if (!CollectionUtils.isEmpty(taskList)) {
                for (Task task : taskList) {
                    if (Objects.nonNull(task.getAssignee()) && task.getAssignee()
                            .equals(requestUser.getId().toString())) {
                        m.put("taskId", task.getId());
                        m.put("myself", true);
                        break;
                    } else {
                        List<IdentityLink> identityLinkList = taskService.getIdentityLinksForTask(task.getId());
                        if (!CollectionUtils.isEmpty(identityLinkList)) {
                            for (IdentityLink i : identityLinkList) {
                                if (i.getUserId().equals(requestUser.getId().toString())) {
                                    m.put("taskId", task.getId());
                                    m.put("myself", true);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
        return list;
    }

    /**
     * 获取下一级审批人
     *
     * @param type
     * @param flowTaskResult
     * @param crmProjectResult
     * @param crmDetailId
     * @return
     */
    public List<String> getNextApproveUserIds(TFCustomTypeEnum type, FlowTaskResult flowTaskResult,
            FlowTaskResult currFlowTaskResult, CrmProjectResult crmProjectResult, Long crmDetailId) {
        List<String> approveUserIds = new ArrayList<>();
        if (type == TFCustomTypeEnum.OFFICE_SOP_FLOW) {//教务处sop流程待审核人
            ServiceScopeEnum scopeEnum = tbSopInfoService.findServiceScope(type, flowTaskResult);
            if (Objects.nonNull(scopeEnum) && scopeEnum == ServiceScopeEnum.SCAN_MARK) {//扫描+阅卷,12345是项目经理
                for (UserArchivesDto u : crmProjectResult.getProjectManagerList()) {
                    approveUserIds.add(u.getUserId().toString());
                }
            } else if (Objects.nonNull(scopeEnum) && scopeEnum == ServiceScopeEnum.SCAN) {
                if (currFlowTaskResult.getSetup().intValue() < 3) {
                    for (UserArchivesDto u : crmProjectResult.getProjectManagerList()) {
                        approveUserIds.add(u.getUserId().toString());
                    }
                } else {
                    Objects.requireNonNull(crmProjectResult.getRegionManagerId(), "未设置大区经理");
                    try {
                        if (currFlowTaskResult.getSetup().intValue() == 4) {
                            List<Long> projectUserList = crmProjectResult.getProjectManagerList().stream()
                                    .map(s -> s.getUserId()).collect(Collectors.toList());
                            if (!org.apache.commons.collections4.CollectionUtils.isEqualCollection(
                                    Arrays.asList(crmProjectResult.getRegionManagerId()), projectUserList)) {
                                approveUserIds.addAll(projectUserList.stream().map(s -> String.valueOf(s))
                                        .collect(Collectors.toList()));
                            } else {
                                approveUserIds.add(crmProjectResult.getRegionManagerId().toString());
                            }
                        } else {
                            approveUserIds.add(crmProjectResult.getRegionManagerId().toString());
                        }
                        tbUserArchivesService.findByUserId(crmProjectResult.getRegionManagerId());
                        List<AllocationParam> allocationParams = new ArrayList<>();
                        allocationParams.add(new AllocationParam(SopRoleTypeEnum.PROJECT_MANAGER,
                                approveUserIds.stream().map(s -> Long.parseLong(s)).collect(Collectors.toList()),
                                "[系统]"));
                        if (!CollectionUtils.isEmpty(crmProjectResult.getEngineerList())) {
                            allocationParams.add(new AllocationParam(SopRoleTypeEnum.ENGINEER,
                                    crmProjectResult.getEngineerList().stream().map(s -> s.getUserId())
                                            .collect(Collectors.toList()), "[系统]"));
                        }
                        if (!CollectionUtils.isEmpty(crmProjectResult.getRegionCoordinatorList())) {
                            allocationParams.add(new AllocationParam(SopRoleTypeEnum.REGION_COORDINATOR,
                                    Arrays.asList(crmProjectResult.getRegionCoordinatorList().get(0).getUserId()),
                                    "[系统]"));
                        }
                        tbUserArchivesAllocationService.editCrmAllocation(
                                new UserArchivesAllocationParam(allocationParams, crmDetailId));
                    } catch (Exception e) {
                        log.error(SystemConstant.LOG_ERROR, e);
                        if (e instanceof ApiException) {
                            ResultUtil.error((ApiException) e, ((ApiException) e).getCode(),
                                    "人员档案里未找到派单号[" + crmProjectResult.getCrmNo() + "]大区经理角色");
                        } else {
                            ResultUtil.error("人员档案里未找到派单号[" + crmProjectResult.getCrmNo() + "]大区经理角色");
                        }
                    }
                }
            }
        } else if (type == TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW) {//研究生sop流程待审核人
            if (currFlowTaskResult.getSetup().intValue() == 1) {
                approveUserIds = Arrays.asList(String.valueOf(crmProjectResult.getRegionManagerId()));
            } else if (currFlowTaskResult.getSetup().intValue() == 2) {
                approveUserIds = crmProjectResult.getProjectManagerList().stream()
                        .map(s -> String.valueOf(s.getUserId())).collect(Collectors.toList());
                approveUserIds.add(String.valueOf(crmProjectResult.getRegionManagerId()));
            } else {
                approveUserIds = crmProjectResult.getProjectManagerList().stream()
                        .map(s -> String.valueOf(s.getUserId())).collect(Collectors.toList());
            }
        }
        return approveUserIds;
    }

    /**
     * 保存提醒/延时任务
     *
     * @param map
     */
    @Override
    @Transactional
    public void saveJobRemind(Map<String, Object> map) throws InterruptedException {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        TFCustomFlow tfCustomFlow = (TFCustomFlow) map.get(SystemConstant.FLOW_CUSTOM);
        Optional.ofNullable(tfCustomFlow).orElseThrow(() -> ExceptionResultEnum.FLOW_CUSTOM_NO_DATA.exception());

        TFCustomFlowEntity tfCustomFlowEntity = (TFCustomFlowEntity) map.get(SystemConstant.FLOW_ENTITY);
        Optional.ofNullable(tfCustomFlowEntity).orElseThrow(() -> ExceptionResultEnum.FLOW_ENTITY_NO_DATA.exception());

        TFFlowApprove tfFlowApprove = (TFFlowApprove) map.get(SystemConstant.FLOW_APPROVE);
        Optional.ofNullable(tfFlowApprove).orElseThrow(() -> ExceptionResultEnum.FLOW_APPROVE_NO_DATA.exception());

        TFFlowLog tfFlowLog = (TFFlowLog) map.get(SystemConstant.FLOW_APPROVE_LOG);
        Optional.ofNullable(tfFlowLog).orElseThrow(() -> ExceptionResultEnum.FLOW_APPROVE_LOG_NO_DATA.exception());

        Long sopId = (Long) map.get(SystemConstant.SOP_ID);
        List<Task> taskList = taskService.createTaskQuery()
                .processInstanceId(String.valueOf(tfCustomFlowEntity.getFlowId())).list();
        if (!CollectionUtils.isEmpty(taskList) && (tfFlowApprove.getStatus() != FlowStatusEnum.FINISH
                && tfFlowApprove.getStatus() != FlowStatusEnum.END)) {
            Long processLimitedTime = null;
            String execField = null;
            if (tfCustomFlow.getType() == TFCustomTypeEnum.OFFICE_SOP_FLOW) {//教务处sop
                for (Task task : taskList) {
                    tsJobRemindService.updateJobRemind(Long.parseLong(task.getId()));
                }
                FlowTaskResult flowTaskResult = this.getFormProperties(tfCustomFlowEntity, 1);
                if (tfFlowApprove.getSetup().intValue() >= 1 && tfFlowApprove.getSetup().intValue() <= 2) {//扫描准备流程
                    processLimitedTime = this.getProcessLimitedTime(flowTaskResult,
                            ProcessLimitedEnum.SCAN_START_TIME.getKey());
                    execField = ProcessLimitedEnum.SCAN_START_TIME.getKey();
                    createJobRemind(execField, processLimitedTime, taskList, tfCustomFlowEntity, tfCustomFlow,
                            tfFlowLog, sysUser.getId(), "扫描准备");
                }
                if (tfFlowApprove.getSetup().intValue() >= 1 && tfFlowApprove.getSetup().intValue() <= 3) {//扫描收尾检查
                    processLimitedTime = this.getProcessLimitedTime(flowTaskResult,
                            ProcessLimitedEnum.SCAN_END_TIME.getKey());
                    execField = ProcessLimitedEnum.SCAN_END_TIME.getKey();
                    createJobRemind(execField, processLimitedTime, taskList, tfCustomFlowEntity, tfCustomFlow,
                            tfFlowLog, sysUser.getId(), "扫描收尾");
                }
                if (tfFlowApprove.getSetup().intValue() >= 1 && tfFlowApprove.getSetup().intValue() <= 4) {//阅卷参数检查
                    processLimitedTime = this.getProcessLimitedTime(flowTaskResult,
                            ProcessLimitedEnum.MARK_START_TIME.getKey());
                    execField = ProcessLimitedEnum.MARK_START_TIME.getKey();
                    createJobRemind(execField, processLimitedTime, taskList, tfCustomFlowEntity, tfCustomFlow,
                            tfFlowLog, sysUser.getId(), "阅卷参数检查");
                }
                if (tfFlowApprove.getSetup().intValue() >= 1 && tfFlowApprove.getSetup().intValue() <= 5) {//阅卷收尾检查
                    processLimitedTime = this.getProcessLimitedTime(flowTaskResult,
                            ProcessLimitedEnum.MARK_END_TIME.getKey());
                    execField = ProcessLimitedEnum.MARK_END_TIME.getKey();
                    createJobRemind(execField, processLimitedTime, taskList, tfCustomFlowEntity, tfCustomFlow,
                            tfFlowLog, sysUser.getId(), "阅卷收尾检查");
                }
            } else if (tfCustomFlow.getType() == TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW) {//研究生sop
                for (Task task : taskList) {
                    tsJobRemindService.updateJobRemind(Long.parseLong(task.getId()));
                }
                FlowTaskResult flowTaskResult = this.getFormProperties(tfCustomFlowEntity, 2);
                if (tfFlowApprove.getSetup().intValue() >= 3 && tfFlowApprove.getSetup().intValue() <= 5) {//扫描准备流程
                    processLimitedTime = this.getProcessLimitedTime(flowTaskResult,
                            ProcessLimitedEnum.SCAN_START_TIME.getKey());
                    execField = ProcessLimitedEnum.SCAN_START_TIME.getKey();
                    createJobRemind(execField, processLimitedTime, taskList, tfCustomFlowEntity, tfCustomFlow,
                            tfFlowLog, sysUser.getId(), "扫描准备");
                }
                if (tfFlowApprove.getSetup().intValue() >= 3 && tfFlowApprove.getSetup().intValue() <= 6) {//扫描收尾检查
                    processLimitedTime = this.getProcessLimitedTime(flowTaskResult,
                            ProcessLimitedEnum.SCAN_END_TIME.getKey());
                    execField = ProcessLimitedEnum.SCAN_END_TIME.getKey();
                    createJobRemind(execField, processLimitedTime, taskList, tfCustomFlowEntity, tfCustomFlow,
                            tfFlowLog, sysUser.getId(), "校验收尾");
                }
                if (tfFlowApprove.getSetup().intValue() >= 3 && tfFlowApprove.getSetup().intValue() <= 7) {//阅卷参数检查
                    processLimitedTime = this.getProcessLimitedTime(flowTaskResult,
                            ProcessLimitedEnum.MARK_START_TIME.getKey());
                    execField = ProcessLimitedEnum.MARK_START_TIME.getKey();
                    createJobRemind(execField, processLimitedTime, taskList, tfCustomFlowEntity, tfCustomFlow,
                            tfFlowLog, sysUser.getId(), "评卷准备");
                }
                if (tfFlowApprove.getSetup().intValue() >= 3 && tfFlowApprove.getSetup().intValue() <= 10) {//阅卷收尾检查
                    processLimitedTime = this.getProcessLimitedTime(flowTaskResult,
                            ProcessLimitedEnum.MARK_END_TIME.getKey());
                    execField = ProcessLimitedEnum.MARK_END_TIME.getKey();
                    createJobRemind(execField, processLimitedTime, taskList, tfCustomFlowEntity, tfCustomFlow,
                            tfFlowLog, sysUser.getId(), "评卷收尾");
                }
            }
            tbSopInfoService.saveSopPlanDate(this.getFormProperties(tfCustomFlowEntity, 1), sopId,
                    tfCustomFlowEntity.getFlowId(), sysUser.getId(), tfCustomFlow.getType());
        }
    }

    /**
     * 创建定时任务
     *
     * @param execField
     * @param processLimitedTime
     * @param taskList
     * @param tfCustomFlowEntity
     * @param tfCustomFlow
     * @param tfFlowLog
     * @param userId
     * @param smsTaskName
     */
    protected void createJobRemind(String execField, Long processLimitedTime, List<Task> taskList,
            TFCustomFlowEntity tfCustomFlowEntity, TFCustomFlow tfCustomFlow, TFFlowLog tfFlowLog, Long userId,
            String smsTaskName) {
        if (Objects.nonNull(execField) && Objects.nonNull(processLimitedTime)) {
            for (Task task : taskList) {
                List<TSJobRemind> tsJobRemindList = new ArrayList<>();
                if (Objects.nonNull(task.getAssignee())) {
                    tsJobRemindList.add(new TSJobRemind(JobQuartzTypeEnum.FLOW, tfCustomFlowEntity.getCode(),
                            Long.parseLong(task.getId()), tfCustomFlow.getType().getTitle() + ";" + smsTaskName,
                            JobTypeEnum.BEFORE, Long.parseLong(task.getAssignee()), tfFlowLog.getApproveOperation(),
                            processLimitedTime, execField, userId, tfCustomFlowEntity.getCrmNo()));
                    tsJobRemindList.add(new TSJobRemind(JobQuartzTypeEnum.FLOW, tfCustomFlowEntity.getCode(),
                            Long.parseLong(task.getId()), tfCustomFlow.getType().getTitle() + ";" + smsTaskName,
                            JobTypeEnum.AFTER, Long.parseLong(task.getAssignee()), tfFlowLog.getApproveOperation(),
                            processLimitedTime, execField, userId, tfCustomFlowEntity.getCrmNo()));
                } else {
                    List<IdentityLink> identityLinkList = taskService.getIdentityLinksForTask(task.getId());
                    if (!CollectionUtils.isEmpty(identityLinkList)) {
                        for (IdentityLink i : identityLinkList) {
                            tsJobRemindList.add(new TSJobRemind(JobQuartzTypeEnum.FLOW, tfCustomFlowEntity.getCode(),
                                    Long.parseLong(i.getTaskId()),
                                    tfCustomFlow.getType().getTitle() + ";" + smsTaskName, JobTypeEnum.BEFORE,
                                    Long.parseLong(i.getUserId()), tfFlowLog.getApproveOperation(), processLimitedTime,
                                    execField, userId, tfCustomFlowEntity.getCrmNo()));
                            tsJobRemindList.add(new TSJobRemind(JobQuartzTypeEnum.FLOW, tfCustomFlowEntity.getCode(),
                                    Long.parseLong(i.getTaskId()),
                                    tfCustomFlow.getType().getTitle() + ";" + smsTaskName, JobTypeEnum.AFTER,
                                    Long.parseLong(i.getUserId()), tfFlowLog.getApproveOperation(), processLimitedTime,
                                    execField, userId, tfCustomFlowEntity.getCrmNo()));
                        }
                    }
                }
                tsJobRemindService.saveJobRemind(tsJobRemindList);
            }
        }
    }

    /**
     * sop编辑
     *
     * @param id
     * @return
     */
    @Override
    public FlowResult sopEdit(Long id) throws InterruptedException {
        Optional.ofNullable(id).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("sopId不能为空"));
        TBSopInfo tbSopInfo = this.getById(id);
        Optional.ofNullable(tbSopInfo).orElseThrow(() -> ExceptionResultEnum.SOP_INFO_NO_DATA.exception());

        TFCustomFlowEntity tfCustomFlowEntity = tfCustomFlowEntityService.findByCode(tbSopInfo.getSopNo());
        TFFlowApprove tfFlowApprove = tfFlowApproveService.findByFlowId(tfCustomFlowEntity.getFlowId());
        FlowResult flowResult = JSONObject.parseObject(tfCustomFlowEntity.getFlowProcessVar(), FlowResult.class);
        FlowResult flowResultNew = new FlowResult();
        BeanUtils.copyProperties(flowResult, flowResultNew);
        LinkedHashMap<String, FlowTaskResult> setupMap = flowResult.getSetupMap();
        LinkedHashMap<String, FlowTaskResult> setupMapNew = new LinkedHashMap<>(setupMap.size());
        for (Map.Entry<String, FlowTaskResult> entry : setupMap.entrySet()) {
            if (entry.getValue().getSetup().intValue() > 0) {
                if (entry.getValue().getSetup().intValue() == 1 && (tfFlowApprove.getStatus() == FlowStatusEnum.FINISH
                        || tfFlowApprove.getStatus() == FlowStatusEnum.END)) {
                    for (FlowFormWidgetResult f : entry.getValue().getFormProperty()) {
                        if (f.getFormId().contains(ProcessLimitedEnum.SERVICE_SCOPE_RADIO.getKey()) || f.getFormId()
                                .contains(ProcessLimitedEnum.DEPLOY_WAY_RADIO.getKey()) || f.getFormId()
                                .contains(ProcessLimitedEnum.MARK_WAY_RADIO.getKey()) || f.getFormId()
                                .contains(ProcessLimitedEnum.SCAN_NET_RADIO.getKey())) {
                            f.setReadable(true);
                            f.setWritable(false);
                        }
                    }
                }
                if (tfFlowApprove.getSetup().intValue() != 0 && tfFlowApprove.getSetup().intValue() < entry.getValue()
                        .getSetup().intValue()) {
                    break;
                } else if (entry.getValue().getSetup().intValue() != tfFlowApprove.getSetup().intValue()) {
                    setupMapNew.put(entry.getKey(), entry.getValue());
                }
            }
        }
        flowResultNew.setSetupMap(setupMapNew);

        TBCrmDetail tbCrmDetail = tbCrmDetailService.findBySopNo(tfCustomFlowEntity.getCode());
        CrmProjectResult crmProjectResult = tbCrmService.findCrmProjectInfo(
                Objects.nonNull(tbCrmDetail) ? tbCrmDetail.getSopNo() : null,
                Objects.nonNull(tbCrmDetail) ? tbCrmDetail.getId() : null, tfCustomFlowEntity.getCrmNo());
        Optional.ofNullable(crmProjectResult).orElseThrow(() -> ExceptionResultEnum.CRM_NO_NO_DATA.exception());

        flowResultNew.setCrmInfo(crmProjectResult);
        return flowResultNew;
    }

    /**
     * sop保存
     *
     * @param sopSaveParam
     * @return
     */
    @Override
    @Transactional
    public Boolean sopSave(SopSaveParam sopSaveParam) throws InterruptedException {
        TBSopInfo tbSopInfo = this.getById(sopSaveParam.getId());
        Optional.ofNullable(tbSopInfo).orElseThrow(() -> ExceptionResultEnum.SOP_INFO_NO_DATA.exception());

        TFCustomFlowEntity tfCustomFlowEntity = tfCustomFlowEntityService.findByCode(tbSopInfo.getSopNo());
        String oldFlowProcessVar = tfCustomFlowEntity.getFlowProcessVar();
        TFFlowApprove tfFlowApprove = tfFlowApproveService.findByFlowId(tfCustomFlowEntity.getFlowId());
        TFCustomFlow tfCustomFlow = tfCustomFlowService.findById(tfCustomFlowEntity.gettFCustomFlowId());

        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        FlowResult flowResultParam = JSONObject.parseObject(sopSaveParam.getFormProperties(), FlowResult.class);

        LinkedHashMap<String, FlowTaskResult> setupParamMap = flowResultParam.getSetupMap();
        FlowTaskResult flowTaskResultNew = null;
        for (Map.Entry<String, FlowTaskResult> entry : setupParamMap.entrySet()) {
            if (entry.getValue().getSetup().intValue() != 0) {//更新动态表单字段和处理时限
                tbSopInfoService.saveDynamicSop(tfCustomFlow, tfCustomFlowEntity.getCode(),
                        tfCustomFlowEntity.getFlowId(), entry.getValue());
                if (entry.getValue().getSetup().intValue() == 1) {
                    flowTaskResultNew = entry.getValue();
                }
            }
        }
        ServiceScopeEnum scopeEnum = tbSopInfoService.findServiceScope(tfCustomFlow.getType(), flowTaskResultNew);

        FlowResult flowResult = JSONObject.parseObject(tfCustomFlowEntity.getFlowProcessVar(), FlowResult.class);
        FlowTaskResult flowTaskResultOld = this.getFormProperties(tfCustomFlowEntity, 1);
        ServiceScopeEnum scopeOldEnum = tbSopInfoService.findServiceScope(tfCustomFlow.getType(), flowTaskResultOld);
        LinkedHashMap<String, FlowTaskResult> setupMap = flowResult.getSetupMap();
        for (Map.Entry<String, FlowTaskResult> entry : setupMap.entrySet()) {
            if (Objects.nonNull(setupParamMap.get(entry.getKey()))) {
                setupMap.put(entry.getKey(), setupParamMap.get(entry.getKey()));
            }
        }
        flowResult.setSetupMap(setupMap);
        tfCustomFlowEntity.updateInfo(sysUser.getId());
        tfCustomFlowEntity.setFlowProcessVar(JacksonUtil.parseJson(flowResult));

        TBCrmDetail tbCrmDetail = tbCrmDetailService.findBySopNo(tfCustomFlowEntity.getCode());
        Objects.requireNonNull(tbCrmDetail, "未找到派单sop信息");

        List<Long> enginessUserIds = new ArrayList<>(), regionCoordinatorUserIds = new ArrayList<>(), projectManagerUserIds = new ArrayList<>();
        FlowTaskResult flowTaskResult = null;
        if (tfCustomFlow.getType() == TFCustomTypeEnum.OFFICE_SOP_FLOW) {
            flowTaskResult = this.getFormProperties(tfCustomFlowEntity, 1);
        } else if (tfCustomFlow.getType() == TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW) {
            flowTaskResult = this.getFormProperties(tfCustomFlowEntity, 2);
        }
        List<FlowFormWidgetResult> flowFormWidgetResultList = flowTaskResult.getFormProperty();
        for (FlowFormWidgetResult f : flowFormWidgetResultList) {
            if (f.getFormId().contains(ProcessLimitedEnum.SCAN_START_TIME.getKey()) && Objects.nonNull(f.getValue())
                    && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                String value = jsonObject.getString(SystemConstant.VALUE);
                tbCrmDetail.setScanStartTime(Long.parseLong(value));
            } else if (f.getFormId().contains(ProcessLimitedEnum.SCAN_END_TIME.getKey()) && Objects.nonNull(
                    f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                String value = jsonObject.getString(SystemConstant.VALUE);
                tbCrmDetail.setScanEndTime(Long.parseLong(value));
                if (Objects.nonNull(tbCrmDetail.getScanStartTime())
                        && tbCrmDetail.getScanEndTime().longValue() <= tbCrmDetail.getScanStartTime().longValue()) {
                    throw ExceptionResultEnum.ERROR.exception("扫描结束时间不能小于等于扫描开始时间");
                }
            } else if (f.getFormId().contains(ProcessLimitedEnum.MARK_START_TIME.getKey()) && Objects.nonNull(
                    f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                String value = jsonObject.getString(SystemConstant.VALUE);
                tbCrmDetail.setMarkPaperStartTime(Long.parseLong(value));
                if (Objects.nonNull(tbCrmDetail.getScanEndTime())
                        && tbCrmDetail.getMarkPaperStartTime().longValue() <= tbCrmDetail.getScanEndTime()
                        .longValue()) {
                    throw ExceptionResultEnum.ERROR.exception("阅卷开始时间不能小于等于扫描结束时间");
                }
            } else if (f.getFormId().contains(ProcessLimitedEnum.MARK_END_TIME.getKey()) && Objects.nonNull(
                    f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                String value = jsonObject.getString(SystemConstant.VALUE);
                tbCrmDetail.setMarkPaperEndTime(Long.parseLong(value));
                if (Objects.nonNull(tbCrmDetail.getMarkPaperStartTime())
                        && tbCrmDetail.getMarkPaperEndTime().longValue() <= tbCrmDetail.getMarkPaperStartTime()
                        .longValue()) {
                    throw ExceptionResultEnum.ERROR.exception("阅卷结束时间不能小于等于阅卷开始时间");
                }
            } else if (f.getFormId().contains(ProcessLimitedEnum.ENGINEER_USERS_ID.getKey()) && Objects.nonNull(
                    f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                JSONArray jsonArray = jsonObject.getJSONArray(SystemConstant.VALUE);
                for (int i = 0; i < jsonArray.size(); i++) {
                    enginessUserIds.add(Long.parseLong(jsonArray.getString(i)));
                }
            } else if (f.getFormId().contains(ProcessLimitedEnum.REGION_COORDINATOR_ID.getKey()) && Objects.nonNull(
                    f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                String value = jsonObject.getString(SystemConstant.VALUE);
                regionCoordinatorUserIds.add(Long.parseLong(value));
            } else if (f.getFormId().contains(ProcessLimitedEnum.PROJECT_MANAGER_ID.getKey()) && Objects.nonNull(
                    f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                String value = jsonObject.getString(SystemConstant.VALUE);
                projectManagerUserIds.add(Long.parseLong(value));
            }
        }
        if (tfCustomFlow.getType() == TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW) {
            FlowTaskResult flowTaskResultTemp = this.getFormProperties(tfCustomFlowEntity, 1);
            List<FlowFormWidgetResult> flowFormWidgetResultTempList = flowTaskResultTemp.getFormProperty();
            for (FlowFormWidgetResult f : flowFormWidgetResultTempList) {
                if (f.getFormId().contains(ProcessLimitedEnum.ENGINEER_USERS_ID.getKey()) && Objects.nonNull(
                        f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                    JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                    JSONArray jsonArray = jsonObject.getJSONArray(SystemConstant.VALUE);
                    for (int i = 0; i < jsonArray.size(); i++) {
                        enginessUserIds.add(Long.parseLong(jsonArray.getString(i)));
                    }
                } else if (f.getFormId().contains(ProcessLimitedEnum.REGION_COORDINATOR_ID.getKey()) && Objects.nonNull(
                        f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                    JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                    String value = jsonObject.getString(SystemConstant.VALUE);
                    regionCoordinatorUserIds.add(Long.parseLong(value));
                } else if (f.getFormId().contains(ProcessLimitedEnum.PROJECT_MANAGER_ID.getKey()) && Objects.nonNull(
                        f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                    JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                    String value = jsonObject.getString(SystemConstant.VALUE);
                    projectManagerUserIds.add(Long.parseLong(value));
                }
            }
        }
        if (tfFlowApprove.getStatus() != FlowStatusEnum.FINISH && tfFlowApprove.getStatus() != FlowStatusEnum.END) {
            Map<String, Object> map = new HashMap<>();
            CrmProjectResult crmProjectResult = tbCrmService.findCrmProjectInfo(tfCustomFlowEntity.getCode(),
                    tbCrmDetail.getId(), tfCustomFlowEntity.getCrmNo());
            Optional.ofNullable(crmProjectResult).orElseThrow(() -> ExceptionResultEnum.CRM_NO_NO_DATA.exception());

            TFFlowLog tfFlowLog = tfFlowLogService.findByLastFlowLogByFlowId(tfCustomFlowEntity.getFlowId());

            map.put(SystemConstant.FLOW_CUSTOM, tfCustomFlow);
            map.put(SystemConstant.FLOW_ENTITY, tfCustomFlowEntity);
            map.put(SystemConstant.FLOW_APPROVE, tfFlowApprove);
            map.put(SystemConstant.FLOW_APPROVE_LOG, tfFlowLog);
            map.put(SystemConstant.CRM_INFO, crmProjectResult);
            map.put(SystemConstant.SOP_ID, tbSopInfo.getId());
            tbSopInfoService.saveJobRemind(map);

            if (tfCustomFlow.getType() == TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW) {
                tbSopInfoService.saveSopPlanDate(this.getFormProperties(tfCustomFlowEntity, 1), tbSopInfo.getId(),
                        tfCustomFlowEntity.getFlowId(), sysUser.getId(), tfCustomFlow.getType());
            }

            //新增sop日志
            if (!Objects.equals(oldFlowProcessVar, tfCustomFlowEntity.getFlowProcessVar())) {
                TBSopLog tbSopLog = new TBSopLog(tbSopInfo.getId(), SopLogTypeEnum.EDIT, tfFlowApprove.getStatus(),
                        tfFlowApprove.getSetup(), oldFlowProcessVar, tfCustomFlowEntity.getFlowProcessVar(),
                        sysUser.getId());
                tbSopLogService.save(tbSopLog);
            }

            List<AllocationParam> allocationParams = new ArrayList<>();
            if (tfCustomFlow.getType() == TFCustomTypeEnum.OFFICE_SOP_FLOW && scopeEnum != scopeOldEnum) {//教务处sop流程待审核人
                Task task = taskService.createTaskQuery().processInstanceId(tfCustomFlowEntity.getFlowId().toString())
                        .singleResult();
                Optional.ofNullable(task).orElseThrow(() -> ExceptionResultEnum.FLOW_TASK_NO_DATA.exception());

                TFFlowLog tfFlowLogExchange = tfFlowLogService.findByLastFlowLogByTaskId(Long.parseLong(task.getId()),
                        FlowApproveOperationEnum.EXCHANGE);
                if (Objects.nonNull(scopeEnum) && scopeEnum == ServiceScopeEnum.SCAN
                        && tfFlowApprove.getSetup().intValue() > 3) {
                    allocationParams.add(new AllocationParam(SopRoleTypeEnum.PROJECT_MANAGER,
                            Objects.nonNull(tfFlowLogExchange) ?
                                    Arrays.asList(tfFlowLogExchange.getApproveId()) :
                                    Arrays.asList(crmProjectResult.getRegionManagerId()), "[系统]"));
                } else if (Objects.nonNull(scopeEnum) && scopeEnum == ServiceScopeEnum.SCAN_MARK) {
                    allocationParams.add(new AllocationParam(SopRoleTypeEnum.PROJECT_MANAGER,
                            Objects.nonNull(tfFlowLogExchange) ?
                                    Arrays.asList(tfFlowLogExchange.getApproveId()) :
                                    Arrays.asList(crmProjectResult.getProjectManagerList().get(0).getUserId()),
                            "[系统]"));
                    tbSopInfoService.sopApproverExchange(tbCrmDetail.getId(), new SopAllocationParam(
                            new UserArchivesAllocationParam(allocationParams, tbCrmDetail.getId())));
                }
                if (!CollectionUtils.isEmpty(allocationParams)) {
                    tbSopInfoService.sopApproverExchange(tbCrmDetail.getId(), new SopAllocationParam(
                            new UserArchivesAllocationParam(allocationParams, tbCrmDetail.getId())));
                    allocationParams.add(new AllocationParam(SopRoleTypeEnum.ENGINEER, enginessUserIds, "[系统]"));
                    if (!CollectionUtils.isEmpty(crmProjectResult.getRegionCoordinatorList())) {
                        allocationParams.add(new AllocationParam(SopRoleTypeEnum.REGION_COORDINATOR,
                                Arrays.asList(crmProjectResult.getRegionCoordinatorList().get(0).getUserId()), "[系统]"));
                    }
                }
            } else {
                if (!CollectionUtils.isEmpty(projectManagerUserIds)) {
                    allocationParams.add(
                            new AllocationParam(SopRoleTypeEnum.PROJECT_MANAGER, projectManagerUserIds, "[系统]"));
                    tbSopInfoService.sopApproverExchange(tbCrmDetail.getId(), new SopAllocationParam(
                            new UserArchivesAllocationParam(allocationParams, tbCrmDetail.getId())));
                }
                if (!CollectionUtils.isEmpty(enginessUserIds)) {
                    allocationParams.add(new AllocationParam(SopRoleTypeEnum.ENGINEER, enginessUserIds, "[系统]"));
                }
                if (!CollectionUtils.isEmpty(regionCoordinatorUserIds)) {
                    allocationParams.add(
                            new AllocationParam(SopRoleTypeEnum.REGION_COORDINATOR, regionCoordinatorUserIds, "[系统]"));
                }
            }
            if (!CollectionUtils.isEmpty(allocationParams)) {
                tbUserArchivesAllocationService.editCrmAllocation(
                        new UserArchivesAllocationParam(allocationParams, tbCrmDetail.getId()));
            }
        }
        if (!tbDingSubmitService.canUpdateDingSetting(tbSopInfo.getSopNo())) {
            throw ExceptionResultEnum.ERROR.exception("考勤已提交，不能修改时间");
        }
        tbCrmDetail.updateInfo(sysUser.getId());
        tbCrmDetailService.updateById(tbCrmDetail);
        tbDingService.resetDingExceptionBySopNo(tbSopInfo.getSopNo());
        tfCustomFlowEntityService.updateById(tfCustomFlowEntity);
        tbSopInfoService.sopProxySystemApprove(tfCustomFlowEntity.getFlowId(), tbSopInfo.getSopNo());
        return true;
    }

    /**
     * 获取表单内容
     *
     * @param tfCustomFlowEntity
     * @param setup
     * @return
     */
    @Override
    public FlowTaskResult getFormProperties(TFCustomFlowEntity tfCustomFlowEntity, Integer setup) {
        FlowResult flowResult = JSONObject.parseObject(tfCustomFlowEntity.getFlowProcessVar(), FlowResult.class);
        LinkedHashMap<String, FlowTaskResult> setupMap = flowResult.getSetupMap();
        FlowTaskResult flowTaskResult = null;
        for (Map.Entry<String, FlowTaskResult> entry : setupMap.entrySet()) {
            if (entry.getValue().getSetup().intValue() == setup.intValue()) {
                flowTaskResult = entry.getValue();
                break;
            }
        }
        return flowTaskResult;
    }

    /**
     * 获取处理时限时间
     *
     * @param flowTaskResult
     * @param fieldId
     * @return
     */
    public Long getProcessLimitedTime(FlowTaskResult flowTaskResult, String fieldId) {
        Long processLimitedTime = null;
        List<FlowFormWidgetResult> formProperty = flowTaskResult.getFormProperty();
        for (FlowFormWidgetResult f : formProperty) {
            if (f.getFormId().contains(fieldId) && Objects.nonNull(f.getValue()) && !Objects.equals(f.getValue(),
                    "{\"value\":null}")) {
                JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                String value = jsonObject.getString(SystemConstant.VALUE);
                processLimitedTime = Long.parseLong(value);
                break;
            }
        }
        //        if (!fieldId.contains(ProcessLimitedEnum.SERVICE_FINISH_PLAN_BEGIN_DATE.getKey())) {
        //            Optional.ofNullable(processLimitedTime).orElseThrow(() -> ExceptionResultEnum.ERROR.exception(
        //                    ProcessLimitedEnum.convertKeyToEnum(fieldId).getTitle() + "处理时限时间为空"));
        //        }
        return processLimitedTime;
    }

    /**
     * 更新sop计划进场开始时间和结束时间
     *
     * @param flowTaskResult
     * @param sopId
     * @param flowId
     * @param userId
     * @param type
     */
    @Override
    @Transactional
    public void saveSopPlanDate(FlowTaskResult flowTaskResult, Long sopId, Long flowId, Long userId,
            TFCustomTypeEnum type) {
        Long serviceFinishPlanBeginDate = null, serviceFinishPlanEndDate = null;
        if (type == TFCustomTypeEnum.CLOUD_MARK_SOP_FLOW) {
            serviceFinishPlanBeginDate = this.getProcessLimitedTime(flowTaskResult,
                    ProcessLimitedEnum.SERVICE_FINISH_PLAN_BEGIN_DATE.getKey());
            serviceFinishPlanEndDate = this.getProcessLimitedTime(flowTaskResult,
                    ProcessLimitedEnum.SERVICE_FINISH_PLAN_DATE.getKey());
        } else if (type == TFCustomTypeEnum.OFFICE_SOP_FLOW) {
            serviceFinishPlanBeginDate = this.getProcessLimitedTime(flowTaskResult,
                    ProcessLimitedEnum.SCAN_START_TIME.getKey());
            serviceFinishPlanEndDate = this.getProcessLimitedTime(flowTaskResult,
                    ProcessLimitedEnum.MARK_END_TIME.getKey());
        }
        if (Objects.nonNull(serviceFinishPlanBeginDate)) {
            TBSopPlanDateLog tbSopPlanDateLogDb = tbSopPlanDateLogService.getOne(
                    new QueryWrapper<TBSopPlanDateLog>().lambda().eq(TBSopPlanDateLog::getFlowId, flowId)
                            .orderByDesc(TBSopPlanDateLog::getCreateTime).last(" limit 1 "));
            if (Objects.isNull(tbSopPlanDateLogDb)) {
                tbSopPlanDateLogDb = new TBSopPlanDateLog(sopId, flowId, serviceFinishPlanBeginDate,
                        serviceFinishPlanEndDate, userId);
                tbSopPlanDateLogService.save(tbSopPlanDateLogDb);
            } else {
                TBSopPlanDateLog tbSopPlanDateLogNew = new TBSopPlanDateLog();
                BeanUtils.copyProperties(tbSopPlanDateLogDb, tbSopPlanDateLogNew);
                tbSopPlanDateLogNew.setBeginTime(serviceFinishPlanBeginDate);
                tbSopPlanDateLogNew.setEndTime(serviceFinishPlanEndDate);
                if (!tbSopPlanDateLogNew.equals(tbSopPlanDateLogDb)) {
                    tbSopPlanDateLogNew.updateInfo(userId);
                    tbSopPlanDateLogService.save(tbSopPlanDateLogNew);
                }
            }
        }
    }

    /**
     * 根据crmNo查询在途的sop信息
     *
     * @param crmNo
     * @return
     */
    @Override
    public List<String> findFlowPassageByCrmNo(String crmNo) {
        Optional.ofNullable(crmNo).orElseThrow(() -> ExceptionResultEnum.CRM_NO_NO_DATA.exception());
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        return this.baseMapper.findFlowPassageByCrmNo(crmNo, sysUser.getId());
    }

    /**
     * 根据serviceId查询所有sop信息
     *
     * @param serviceId
     * @return
     */
    @Override
    public List<SopInfoResult> findFlowByServiceId(Long serviceId) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        return this.baseMapper.findFlowByServiceId(serviceId, sysUser.getId());
    }

    /**
     * 查询服务范围
     *
     * @param type
     * @param flowTaskResult
     * @return
     */
    @Override
    public ServiceScopeEnum findServiceScope(TFCustomTypeEnum type, FlowTaskResult flowTaskResult) {
        ServiceScopeEnum scopeEnum = null;
        if (type == TFCustomTypeEnum.OFFICE_SOP_FLOW) {//教务处sop流程待审核人
            List<FlowFormWidgetResult> flowFormWidgetResultList = flowTaskResult.getFormProperty();
            for (FlowFormWidgetResult f : flowFormWidgetResultList) {
                if (f.getFormId().contains(ProcessLimitedEnum.SERVICE_SCOPE_RADIO.getKey())) {
                    JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                    String value = jsonObject.getString(SystemConstant.VALUE);
                    scopeEnum = ServiceScopeEnum.valueOf(value);
                    break;
                }
            }
        }
        return scopeEnum;
    }

    /**
     * 根据sop单号查询sop计划时间
     *
     * @param sopNo
     * @return
     */
    @Override
    public SopPlanTimeResult querySopPlanTime(String sopNo) {
        TFCustomFlowEntity tfCustomFlowEntity = tfCustomFlowEntityService.findByCode(sopNo);
        TFCustomFlow tfCustomFlow = tfCustomFlowService.findById(tfCustomFlowEntity.gettFCustomFlowId());

        Task task = taskService.createTaskQuery().processInstanceId(String.valueOf(tfCustomFlowEntity.getFlowId()))
                .singleResult();

        FlowTaskResult currFlowTaskResult = null;
        if (Objects.nonNull(task)) {
            FlowResult flowResult = JSONObject.parseObject(tfCustomFlowEntity.getFlowProcessVar(), FlowResult.class);
            LinkedHashMap<String, FlowTaskResult> setupMap = flowResult.getSetupMap();
            currFlowTaskResult = GsonUtil.fromJson(GsonUtil.toJson(setupMap.get(task.getTaskDefinitionKey())),
                    FlowTaskResult.class);
        }

        Long beginTime = null, endTime = null;
        FlowTaskResult flowTaskResult = tbSopInfoService.getFormProperties(tfCustomFlowEntity, 1);
        List<FlowFormWidgetResult> flowFormWidgetResultList = flowTaskResult.getFormProperty();
        for (FlowFormWidgetResult f : flowFormWidgetResultList) {
            if (f.getFormId().contains(ProcessLimitedEnum.SCAN_START_TIME.getKey()) && Objects.nonNull(f.getValue())
                    && !Objects.equals(f.getValue(), "{\"value\":null}") && Objects.nonNull(currFlowTaskResult)
                    && currFlowTaskResult.getSetup().intValue() <= 3) {
                JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                String value = jsonObject.getString(SystemConstant.VALUE);
                beginTime = Long.parseLong(value);
            } else if (f.getFormId().contains(ProcessLimitedEnum.SCAN_END_TIME.getKey()) && Objects.nonNull(
                    f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}") && Objects.nonNull(
                    currFlowTaskResult) && currFlowTaskResult.getSetup().intValue() <= 3) {
                JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                String value = jsonObject.getString(SystemConstant.VALUE);
                endTime = Long.parseLong(value);
            } else if (f.getFormId().contains(ProcessLimitedEnum.MARK_START_TIME.getKey()) && Objects.nonNull(
                    f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                String value = jsonObject.getString(SystemConstant.VALUE);
                beginTime = Long.parseLong(value);
            } else if (f.getFormId().contains(ProcessLimitedEnum.MARK_END_TIME.getKey()) && Objects.nonNull(
                    f.getValue()) && !Objects.equals(f.getValue(), "{\"value\":null}")) {
                JSONObject jsonObject = JSONObject.parseObject(f.getValue());
                String value = jsonObject.getString(SystemConstant.VALUE);
                endTime = Long.parseLong(value);
            }
        }
        return new SopPlanTimeResult(tfCustomFlow.getType(), beginTime, endTime);
    }

    /**
     * 根据sopId查询用户
     *
     * @param id
     * @return
     */
    @Override
    public List<SysUser> userList(Long id) {
        return this.baseMapper.userList(id);
    }
}
