package com.brilliance.isc.automatic.engine.flow;

import com.alibaba.fastjson.JSON;
import com.brilliance.isc.automatic.engine.config.AutoRouterConfig;
import com.brilliance.isc.automatic.engine.constant.AutomaticConstants;
import com.brilliance.isc.automatic.engine.constant.enums.AutoSrcTypeEnum;
import com.brilliance.isc.automatic.engine.constant.enums.AutoStaEnum;
import com.brilliance.isc.automatic.engine.constant.enums.AutoTypeEnum;
import com.brilliance.isc.automatic.engine.exception.AutomaticException;
import com.brilliance.isc.automatic.engine.model.AutoRoute;
import com.brilliance.isc.automatic.engine.model.FinishFlow;
import com.brilliance.isc.bo.*;
import com.brilliance.isc.bo.auto.AutoFlow;
import com.brilliance.isc.common.api.bch.service.BchService;
import com.brilliance.isc.common.api.bch.service.impl.BchServiceImpl;
import com.brilliance.isc.common.component.DyncDaoService;
import com.brilliance.isc.common.context.SettleContext;
import com.brilliance.isc.common.counter.service.CounterService;
import com.brilliance.isc.common.exception.CommonServiceException;
import com.brilliance.isc.common.lock.BizLocker;
import com.brilliance.isc.common.lock.LockInfo;
import com.brilliance.isc.common.setfeg.service.SetfegService;
import com.brilliance.isc.common.tools.service.ObjectStroeToolService;
import com.brilliance.isc.common.transaction.help.TransactionHelpService;
import com.brilliance.isc.common.user.service.UserService;
import com.brilliance.isc.mda.dao.*;
import com.brilliance.isc.vo.ResponseSet;
import com.brilliance.isc.vo.manager.SessionUserVo;
import com.brilliance.isc.vo.manager.UserVo;
import com.brilliance.isc.warn.WarnService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.ProducerTemplate;
import org.apache.commons.io.FileUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.File;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;

import static com.brilliance.isc.automatic.engine.constant.AutomaticConstants.*;

/**
 * 自动化流程辅助类
 */
@Slf4j
@Component
public class AutoFlowHelper {
    @Resource
    private AutoFlowMapper autoFlowMapper;
    @Resource
    private CounterService counterService;
    @Resource
    private ObjectMapper objectMapper;
    @Resource
    private ObjectStroeToolService objectStroeToolService;
    @Resource
    private ProducerTemplate producerTemplate;
    @Resource
    private BizLocker bizLocker;
    @Resource
    private SptMapper sptMapper;
    @Resource
    private QueMapper queMapper;
    @Resource
    UserService userService;
    @Resource
    private BchService bchService;
    @Resource
    private SetfegService setfegService;
    @Resource
    private TransactionHelpService transactionHelpService;
    @Resource
    private WarnService warnService;
    @Autowired(required = false)
    private RedissonClient redissonClient;
    @Resource
    private DyncDaoService dyncDaoService;
    @Resource
    private UsrntfMapper usrntfMapper;
    @Resource
    private HyfcfgMapper hyfcfgMapper;

    @Value("${sstf.auto.handle.usr:}")
    private String handleUsr;
    @Value("${sstf.auto.relrow.usr:}")
    private String relrowUsr;

    private final FinishFlow FINISH_FLOW = new FinishFlow();
    private static final String AUTO_CACHE_PATH = "sstf/data/auto/";
    private static final ThreadLocal<LockInfo> LOCK = new ThreadLocal<>();
    private static final Long LEASE_TIME = 30L;
    private static final String LOCK_PREFIX = "AUTOFLOW_";

    /**
     * 灵活的基于自己的判断条件确定是否准入（创建自动化流程）
     *
     * @param type     自动化类型
     * @param srctyp   自动化来源表类型
     * @param srcinr   自动化来源表主键
     * @param source   待判断的数据源
     * @param function 准入函数
     */
    public <T> AutoFlow access(AutoTypeEnum type, String srctyp, String srcinr, T source, Function<T, String> function) {
        if (ObjectUtils.isEmpty(type)) {
            log.error("请设置自动化类型");
            return null;
        }
        //通用准入校验
        if (!commonValidate(srctyp, srcinr)) {
            return null;
        }
        String subtyp = function.apply(source);
        if (!StringUtils.hasText(subtyp)) {
            log.error("待办任务准入校验不通过:来源={},来源主键={}", srctyp, srcinr);
            return null;
        }
        if (log.isInfoEnabled()) {
            log.info("待办任务准入通过:来源={},来源主键={},场景={}", srctyp, srcinr, subtyp);
        }
        AutoFlow autoFlow = null;
        try {
            autoFlow = createAutoFlow(type.getCode(), subtyp, srctyp, srcinr);
        } catch (Exception e) {
            //避免自动化准入入库失败导致后台收发报通用流程异常
            log.error("待办任务准入异常:来源={},来源主键={},场景={}", srctyp, srcinr, subtyp, e);
        }
        return autoFlow;
    }

    /**
     * 查状态为0（待执行）和5（继续执行）两种状态的待处理的所有待处理自动化任务
     */
    public List<AutoFlow> selectCandidateAutoFlow(AutoTypeEnum autoTypeEnum) {
        return selectCandidateAutoFlow(autoTypeEnum, -1);
    }

    /**
     * 查状态为0（待执行）和5（继续执行）两种状态的待处理的num条自动化任务
     */
    public List<AutoFlow> selectCandidateAutoFlow(AutoTypeEnum autoTypeEnum, int num) {
        if (ObjectUtils.isEmpty(autoTypeEnum)) {
            log.warn("请设置自动化类型");
            return Collections.emptyList();
        }
        List<AutoFlow> autoFlows = autoFlowMapper.selectWaitAndResume(autoTypeEnum.getCode(), num);
        //分布式锁控制任务并发,筛选可执行的自动化任务
        List<AutoFlow> candidate = new LinkedList<>();
        autoFlows.forEach(autoFlow -> {
            RLock lock;
            try {
                lock = redissonClient.getLock(LOCK_PREFIX + autoFlow.getInr());
                boolean locked = lock.tryLock(1, LEASE_TIME, TimeUnit.SECONDS);
                if (!locked) {
                    if (log.isInfoEnabled()) {
                        log.info("自动化任务[{}]已经被锁,不加入本次待处理队列", autoFlow.getInr());
                    }
                    return;
                }
                if (log.isInfoEnabled()) {
                    log.info("自动化任务[{}]加入本次待处理队列", autoFlow.getInr());
                }
                candidate.add(autoFlow);

            } catch (Exception e) {
                //静默处理
            }
        });
        return candidate;
    }

    /**
     * 异步触发自动化任务
     *
     * @param excutor        线程池
     * @param autoFlow       待处理任务
     * @param countDownLatch 锁
     */
    public void asyncTrigger(ThreadPoolExecutor excutor, AutoFlow autoFlow, CountDownLatch countDownLatch) {
        excutor.execute(() -> {
            try {
                trigger(autoFlow, null);
            } finally {
                countDownLatch.countDown();
            }
        });
    }

    /**
     * 调起自动化流程
     *
     * @param condition 查询具体自动化流程条件，必输
     * @param body      自动化步骤使用的Exchange的body信息，可以为空
     */
    public void trigger(AutoFlow condition, Object body) {
        trigger(condition, body, null);
    }

    /**
     * 调起自动化流程
     *
     * @param condition 查询具体自动化流程条件，必输
     * @param body      自动化步骤使用的Exchange的body信息，可以为空
     * @param context   自动化流程执行需要的额外上下文信息，可以为空
     */
    public void trigger(AutoFlow condition, Object body, Map<String, Object> context) {
        Assert.notNull(condition, "调起指定自动化流程条件不能为空");
        try {
            //锁那条业务，不允许业务操作
            if (!lock(condition)) {
                return;
            }
            initHandleUserInfo(condition);
            producerTemplate.send(AutomaticConstants.AUTOMATIC_START_POINT, exchange -> {
                exchange.setProperty(AutomaticConstants.AUTO_PAR, condition);
                Message in = exchange.getIn();
                if (!ObjectUtils.isEmpty(body)) {
                    in.setBody(body);
                }
                if (!ObjectUtils.isEmpty(context)) {
                    for (Map.Entry<String, Object> entry : context.entrySet()) {
                        exchange.setProperty(entry.getKey(), entry.getValue());
                    }
                }
            });
        } finally {
            this.clearCache();
            //释放业务锁
            unlock();
        }
    }

    /**
     * 避免因为异常导致ThreadLocal未被清理
     */
    private void clearCache() {
        log.info("清理当前线程{}产生的ThreadLocal数据", Thread.currentThread().getName());
        SettleContext.removeUserSession();
        SettleContext.removeSessionUserVo();
        setfegService.clearFeeSet();
        transactionHelpService.clearTransactionStoreSet();
        transactionHelpService.clearLiaallSet();
        warnService.clearWarn();
        BchServiceImpl.removeBranchCacheMap();
        BchServiceImpl.removeBchInrCacheMap();
    }

    /**
     * 加载自动化经办用户信息
     */
    public SessionUserVo initHandleUserInfo(AutoFlow condition) {
        AutoFlow autoFlow = selectAutoFlow(condition);
        SessionUserVo handleUserInfo = getHandleUserInfo(autoFlow.getBranch());
        SettleContext.setSessionUserVo(handleUserInfo);
        return handleUserInfo;
    }

    /**
     * 远程调用自动化复核
     */
    public ResponseSet autoRelrow(String branch, Function<String, ResponseSet> function) {
        SessionUserVo relrowUserInfo = getRelrowUserInfo(branch);
        return autoInvoke(function, relrowUserInfo);
    }

    /**
     * 自动化后台Feign调用辅助方法（譬如自动化复核）
     */
    public ResponseSet autoInvoke(Function<String, ResponseSet> function, SessionUserVo sessionUserVo) {
        if (ObjectUtils.isEmpty(sessionUserVo)) {
            SettleContext.setSessionUserVo("DOS", "2020"); // TODO  待定
            sessionUserVo = SettleContext.getSessionUserVo();
        }
        String head;
        ResponseSet responseSet;
        try {
            head = objectMapper.writeValueAsString(sessionUserVo);
            responseSet = function.apply(head);
            Assert.hasText(responseSet.getRespCode(), "后台自动化服务调用失败:返回信息为空");
            if (!ResponseSet.CODE_SUCCESS.equalsIgnoreCase(responseSet.getRespCode())) {
                throw new CommonServiceException(responseSet.getRespMsg());
            }
        } catch (Exception e) {
            log.error("后台自动化服务调用失败:{}", e.getMessage());
            return ResponseSet.fail("后台自动化服务调用失败:" + e.getMessage());
        }
        return responseSet;
    }

    /**
     * 找最新的que
     */
    public Que getLatestQue(String autoFlowInr) {
        AutoFlow autoFlow = autoFlowMapper.selectByPrimaryKey(autoFlowInr);
        Que que = null;
        if (StringUtils.hasText(autoFlow.getTrninr())) {
            que = queMapper.selectByObjtypAndObjinr("TRN", autoFlow.getTrninr());
        }
        if (que == null) {
            que = queMapper.selectByObjtypAndObjinr("SPT", autoFlow.getSrcinr());
        }
        return que;
    }

    /**
     * 标记当前步骤执行失败
     */
    public void markFail(Exchange exchange, String code, String msg) {
        exchange.getIn().setHeader(CURRENT_STEP_RESULT, code);
        if (msg != null && msg.length() > 255) {
            //防止更新表列lsterr超长异常
            msg = msg.substring(0, 255);
        }
        exchange.setProperty(CURRENT_STEP_EXCEPTION, new AutomaticException(msg));
    }

    /**
     * 标记存盘成功：更新流程信息，删除spt主键信息和维护trn主键信息
     *
     * @param exchange 自动化上下文
     * @param trnInr   待更新的trnInr
     */
    public void markSave(Exchange exchange, String trnInr) {
        AutoFlow curAutoFlow = getCurAutoFlow(exchange);
        if (ObjectUtils.isEmpty(curAutoFlow)) {
            log.error("未查询到对应的自动化信息");
            return;
        }
        markSave(curAutoFlow.getInr(), trnInr);
        curAutoFlow.setTrninr(trnInr);
        setCurAutoFlow(exchange, curAutoFlow);
    }

    /**
     * 标记存盘成功：更新流程信息，删除spt主键信息和维护trn主键信息
     *
     * @param autoFlowInr 自动化任务主键
     * @param trnInr      待更新的trnInr
     */
    public void markSave(String autoFlowInr, String trnInr) {
        if (ObjectUtils.isEmpty(trnInr)) {
            log.error("不做自动化存盘标记：trnInr为空");
            return;
        }
        AutoFlow autoFlow = new AutoFlow();
        autoFlow.setInr(autoFlowInr);
        autoFlow.setTrninr(trnInr);
        updateAutoFlow(autoFlow);
    }

    /**
     * 更新流程信息表相关信息继续执行
     */
    public void updateAutoFlowResume(String autoFlowInr) {
        AutoFlow autoFlow = autoFlowMapper.selectByPrimaryKey(autoFlowInr);
        updateAutoFlowSta(autoFlow, AutoStaEnum.RESUME);
    }

    /**
     * 更新流程信息表相关信息继续执行
     */
    public void updateAutoFlowResumeBySptinr(String sptinr) {
        AutoFlow autoFlow = autoFlowMapper.selectBySrc(AutoSrcTypeEnum.SPT.getCode(), sptinr);
        updateAutoFlowSta(autoFlow, AutoStaEnum.RESUME);
    }

    /**
     * 更新流程信息表相关信息错误
     */
    public void updateAutoFlowERRORBySptinr(String sptinr, String err) {
        AutoFlow autoFlow = autoFlowMapper.selectBySrc(AutoSrcTypeEnum.SPT.getCode(), sptinr);
        autoFlow.setLsterr(err);
        updateAutoFlowSta(autoFlow, AutoStaEnum.ERROR);
    }

    /**
     * 更新流程信息表相关信息错误
     */
    public void updateAutoFlowERROR(String autoFlowInr, String err) {
        AutoFlow autoFlow = autoFlowMapper.selectByPrimaryKey(autoFlowInr);
        autoFlow.setLsterr(err);
        updateAutoFlowSta(autoFlow, AutoStaEnum.ERROR);
    }

    /**
     * 更新流程信息表相关信息暂停
     */
    public void updateAutoFlowPause(String autoFlowInr) {
        AutoFlow autoFlow = autoFlowMapper.selectByPrimaryKey(autoFlowInr);
        updateAutoFlowSta(autoFlow, AutoStaEnum.PAUSE);
    }

    /**
     * 更新流程信息表相关信息成功执行
     */
    public void updateAutoFlowFinish(String autoFlowInr) {
        AutoFlow autoFlow = autoFlowMapper.selectByPrimaryKey(autoFlowInr);
        updateAutoFlowSta(autoFlow, AutoStaEnum.FINISH);
    }

    /**
     * 获取下一步待处理自动化流程
     */
    public AutoFlow getOrCreateAutoFlow(Exchange exchange, AutoRouterConfig config, Supplier supplier) {
        AutoFlow autoFlow = getCurAutoFlow(exchange);
        if (!ObjectUtils.isEmpty(autoFlow)) {
            return autoFlow;
        }
        AutoFlow para = (AutoFlow) supplier.get();
        if (ObjectUtils.isEmpty(para)) {
            log.error("从Exchange上下文未获取到下步流程，并且传入的计算参数为空");
            return null;
        }
        //执行到这里说明是中间转人工，后续自动化流程继续调起，先从库里查询最近执行到哪一步
        autoFlow = autoFlowMapper.selectSelective(para);
        if (ObjectUtils.isEmpty(autoFlow)) {
            log.error("没有查询到满足条件的自动化任务:{}", para);
            return null;
        }
        if (AutoStaEnum.FINISH.getCode().equals(autoFlow.getSta())) {
            if (log.isInfoEnabled()) {
                log.info("当前自动化任务已经结束");
            }
            autoFlow = FINISH_FLOW;
            setCurAutoFlow(exchange, autoFlow);
            return autoFlow;
        }
        AutoRoute autoRoute;
        if (AutoStaEnum.ERROR.getCode().equals(autoFlow.getSta())) {
            //如果流程是续作，表示从当前步骤执行
            if (log.isInfoEnabled()) {
                log.info("由于上次执行失败，从当前自动化步骤开始执行");
            }
            //从对象存储拿之前缓存的自动化信息，加载Exchange上下文
            loadExchange(exchange, autoFlow.getInr());
            autoRoute = new AutoRoute();
            autoRoute.setStep(autoFlow.getStep());
            autoRoute.setName(autoFlow.getName());
            autoRoute.setAddress(autoFlow.getAddress());
        } else {
            if (AutoStaEnum.RESUME.getCode().equals(autoFlow.getSta())) {
                //从对象存储拿之前缓存的自动化信息，加载Exchange上下文
                loadExchange(exchange, autoFlow.getInr());
            }
            //计算下一步流程
            autoRoute = findNextAutoRoute(config, autoFlow.getStep());
        }

        if (ObjectUtils.isEmpty(autoRoute)) {
            return null;
        }
        autoRoute.fillAutoFlow(autoFlow);
        setCurAutoFlow(exchange, autoFlow);
        return autoFlow;

    }

    /**
     * 创建流程信息
     */
    public AutoFlow createAutoFlow(String type, String subtyp, String srctyp, String srcinr) {
        AutoFlow autoFlow = new AutoFlow();
        autoFlow.setStep(-1);
        autoFlow.setType(type);
        autoFlow.setSubtyp(subtyp);
        autoFlow.setSrctyp(srctyp);
        autoFlow.setSrcinr(srcinr);
        autoFlow.setSta(AutoStaEnum.WAITING.getCode());
        autoFlow.setCreatetime(new Date());
        if (AutoSrcTypeEnum.SPT.getCode().equals(srctyp)) {
            Spt spt = sptMapper.getSptByInr(srcinr);
            autoFlow.setBranch(getBranch(spt));
        } else {
            log.error("记录归属机构失败：不能识别{}自动化来源", srctyp);
        }
        return createAutoFlow(autoFlow);
    }

    /**
     * 创建流程信息
     */
    public AutoFlow createAutoFlow(AutoFlow autoFlow) {
        if (!StringUtils.hasText(autoFlow.getInr())) {
            String inr = counterService.dbCounterFormat("AUTOFLOW");
            autoFlow.setInr(inr);
        }
        int i = autoFlowMapper.insertSelective(autoFlow);
        if (i <= 0) {
            log.error("插入自动化流程信息失败");
            return null;
        }
        return autoFlow;
    }

    /**
     * 查询流程信息
     */
    public AutoFlow selectAutoFlow(AutoFlow autoFlow) {
        return autoFlowMapper.selectSelective(autoFlow);
    }

    /**
     * 当前流程信息放入Exchange上下文AUTO_FLOW中
     */
    public void setCurAutoFlow(Exchange exchange, AutoFlow autoFlow) {
        exchange.setProperty(AUTO_FLOW, autoFlow);
    }

    /**
     * 从Exchange上下文AUTO_FLOW中获取流程信息,并且需要从数据库同步最新状态
     */
    public AutoFlow getCurAutoFlow(Exchange exchange) {
        AutoFlow autoFlow = exchange.getProperty(AUTO_FLOW, AutoFlow.class);
        if (ObjectUtils.isEmpty(autoFlow)) {
            return null;
        }
        AutoFlow source = selectAutoFlow(autoFlow);
        if (ObjectUtils.isEmpty(source)) {
            return autoFlow;
        }
        autoFlow.setSrctyp(source.getSrctyp());
        autoFlow.setSrcinr(source.getSrcinr());
        autoFlow.setTrninr(source.getTrninr());
        autoFlow.setOwnref(source.getOwnref());
        autoFlow.setBranch(source.getBranch());
        autoFlow.setSta(source.getSta());
        return autoFlow;
    }

    /**
     * 更新流程信息表中的状态和更新时间两个字段信息
     */
    public void updateAutoFlowSta(AutoFlow autoFlow, AutoStaEnum staEnum, Exchange exchange) {
        if (AutoStaEnum.ERROR.equals(staEnum) || AutoStaEnum.PAUSE.equals(staEnum)) {
            if (log.isInfoEnabled()) {
                log.info("{}:开始缓存{}流程信息", staEnum.getDesc(), autoFlow);
            }
            if (!ObjectUtils.isEmpty(autoFlow)) {
                storeCache(exchange, autoFlow.getInr());
            }
            String step = exchange.getProperty(CURRENT_STEP_KEY, String.class);
            if (autoFlow != null) {
                createNotification(autoFlow,step);
            }
        }
        updateAutoFlowSta(autoFlow, staEnum);
    }

    /**
     * 更新流程信息表中的状态和更新时间两个字段信息
     */
    public void updateAutoFlowSta(AutoFlow autoFlow, AutoStaEnum staEnum) {
        if (ObjectUtils.isEmpty(autoFlow)) {
            return;
        }
        if (AutoStaEnum.FINISH.equals(staEnum)) {
            //清理可能存在的暂存数据
            deleteCache(autoFlow.getInr());
        }
        autoFlow.setSta(staEnum.getCode());
        autoFlow.setUpdatetime(new Date());
        updateAutoFlow(autoFlow);
    }

    /**
     * 更新流程信息表相关信息通用方法
     */
    public void updateAutoFlow(AutoFlow autoFlow) {
        autoFlowMapper.updateByPrimaryKeySelective(autoFlow);
    }

    /**
     * 二分法获取下一步路由步骤。如果没有下一步路由，返回空
     */
    public AutoRoute findNextAutoRoute(AutoRouterConfig config, int currentNumber) {
        int left = 0;
        int right = config.size() - 1;
        int resultIndex = -1;
        List<AutoRoute> steps = config.getRoutes();
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (steps.get(mid).getStep() == currentNumber) {
                //找到当前，尝试找下一步
                if (mid < steps.size() - 1) {
                    resultIndex = mid + 1;
                    break;
                } else {
                    //已经是最后一步，没有下一步了
                    return null;
                }
            } else if (steps.get(mid).getStep() < currentNumber) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        //如果没有找到，但找到一个比他大的步骤，返回它
        if (resultIndex == -1 && left < steps.size() && steps.get(left).getStep() > currentNumber) {
            resultIndex = left;
        }
        if (resultIndex == -1) {
            return null;
        }
        return steps.get(resultIndex);
    }

    /**
     * 准入通用校验
     */
    protected boolean commonValidate(String srctyp, String srcinr) {
        if (ObjectUtils.isEmpty(srctyp)) {
            log.error("自动化来源表类型为空");
            return false;
        }
        if (ObjectUtils.isEmpty(srcinr)) {
            log.error("自动化来源表主键为空");
            return false;
        }
        AutoFlow condition = new AutoFlow();
        condition.setSrctyp(srctyp);
        condition.setSrcinr(srctyp);
        AutoFlow autoFlow = autoFlowMapper.selectSelective(condition);
        if (!ObjectUtils.isEmpty(autoFlow)) {
            log.error("准入通用校验失败：对应的自动化任务已经存在:srctyp={},srcinr={}", srctyp, srcinr);
            return false;
        }
        Map<String, Object> src = dyncDaoService.dyncReadByInr(srctyp, srcinr);
        if (ObjectUtils.isEmpty(src)) {
            log.error("准入通用校验失败：对应的主表信息不存在:srctyp={},srcinr={}", srctyp, srcinr);
            return false;
        }
        return true;
    }

    /**
     * 将Exchange的properties上下文保存，目的是因为失败和暂停，而后续调起来
     */
    private void storeCache(Exchange exchange, String inr) {
        String path = cachePath(inr);
        try {
            Map<String, Object> context = exchange.getProperties();
            //去除非业务信息
            cleanContext(context);
            String contextStr = JSON.toJSONString(context);
            objectStroeToolService.saveObjectFileByString(contextStr, path, null);
        } catch (Exception e) {
            log.error("缓存{}流程信息异常", inr, e);
        }
    }

    /**
     * 中断后重新调起自动化，加载Exchange的properties上下文信息
     */
    private Exchange loadExchange(Exchange curExchange, String inr) {
        String path = cachePath(inr);
        try {
            //获取缓存对象
            if (!objectStroeToolService.isExistObjectFile(path)) {
                log.warn("没有找到{}自动化流程缓存信息,使用当前上下文", inr);
                return curExchange;
            }
            File cacheFile = objectStroeToolService.getObjectFile(path);
            if (cacheFile == null || !cacheFile.exists()) {
                log.error("缓存{}流程信息不存在", inr);
                return curExchange;
            }
            String json = FileUtils.readFileToString(cacheFile, "utf-8");
            Map<String, Object> cacheExchangeProperties = JSON.parseObject(json, Map.class);
            Map<String, Object> curExchangeProperties = curExchange.getProperties();
            for (Map.Entry<String, Object> entry : cacheExchangeProperties.entrySet()) {
                if (!curExchangeProperties.containsKey(entry.getKey())) {
                    curExchange.setProperty(entry.getKey(), entry.getValue());
                }
            }
        } catch (Exception e) {
            log.error("缓存{}流程信息异常", inr, e);
        }
        return curExchange;
    }

    /**
     * 清理缓存
     */
    private void deleteCache(String inr) {
        String path = cachePath(inr);
        if (objectStroeToolService.isExistObjectFile(path)) {
            objectStroeToolService.removeObjectFile(path);
        }
    }

    /**
     * 获取缓存路径
     */
    private String cachePath(String inr) {
        return AUTO_CACHE_PATH + inr;
    }

    /**
     * 去除非业务信息
     */
    private void cleanContext(Map<String, Object> context) {
        context.remove(CURRENT_STEP_EXCEPTION);
        context.remove(CAMEL_EXCEPTION_KEY);
        context.remove(CURRENT_STEP_RESULT);
        context.remove("TryRouteBlock");
        for (Map.Entry<String, Object> entry : context.entrySet()) {
            String key = entry.getKey();
            if (key != null && key.startsWith("Camel")) {
                context.remove(key);
            }
        }
    }

    /**
     * 锁定业务，防止与自动化冲突
     */
    private boolean lock(AutoFlow para) {
        String objTyp;
        String objInr;
        AutoFlow autoFlow = autoFlowMapper.selectSelective(para);
        if (StringUtils.hasText(autoFlow.getTrninr())) {
            objTyp = "TRN";
            objInr = autoFlow.getTrninr();
        } else {
            objTyp = autoFlow.getSrctyp();
            objInr = autoFlow.getSrcinr();
        }
        //锁那条待办SPT，不允许业务操作
        LockInfo lockInfo = buildLockInfo(objTyp, objInr);
        boolean lock = bizLocker.lock(lockInfo);
        if (!lock) {
            log.warn("交易锁定失败:{}", lockInfo);
            return false;
        }
        LOCK.set(lockInfo);
        return true;
    }

    /**
     * 释放自动化任务锁定的业务
     */
    private boolean unlock() {
        LockInfo lockInfo = LOCK.get();
        if (ObjectUtils.isEmpty(lockInfo)) {
            return true;
        }
        boolean unlock = bizLocker.unlock(lockInfo);
        if (!unlock) {
            log.warn("释放锁{}失败", lockInfo);
            return false;
        }
        LOCK.remove();
        return true;
    }

    /**
     * 构建自动化准入后待办任务加锁信息
     */
    private LockInfo buildLockInfo(String objTyp, String objInr) {
        return LockInfo.builder().objInr(objInr)
                .objType(objTyp)
                .usrId("robot")
                .trnName("ROBTSK")
                .reentrant(true)
                .build();
    }

    /**
     * 加载自动化经办用户信息
     */
    private SessionUserVo getHandleUserInfo(String branch) {
        if (!StringUtils.hasText(handleUsr) || !StringUtils.hasText(branch)) {
            log.error("加载自动化经办用户信息失败：用户或者机构信息为空");
            return null;
        }
        UserVo userVo = userService.getUserByLogName(handleUsr);
        SessionUserVo sessionUserVo = new SessionUserVo();
        sessionUserVo.setId(userVo.getId());
        sessionUserVo.setLogName(handleUsr);
        sessionUserVo.setOwnOrgDepartmentNumber(branch);
        return sessionUserVo;
    }

    /**
     * 加载自动化复核用户信息
     */
    private SessionUserVo getRelrowUserInfo(String branch) {
        if (!StringUtils.hasText(relrowUsr) || !StringUtils.hasText(branch)) {
            log.error("加载自动化复核用户信息失败：用户或者机构信息为空");
            return null;
        }
        UserVo userVo = userService.getUserByLogName(relrowUsr);
        SessionUserVo sessionUserVo = new SessionUserVo();
        sessionUserVo.setId(userVo.getId());
        sessionUserVo.setLogName(relrowUsr);
        sessionUserVo.setOwnOrgDepartmentNumber(branch);
        return sessionUserVo;
    }

    /**
     * 根据spt查找自动化任务处理机构
     *
     * @param spt 待办任务
     */
    private String getBranch(Spt spt) {
        String result = "2020";
        if (spt == null) {
            log.warn("Spt记录不存在,返回默认全辖机构2020");
            return result;
        }
        log.info("获取处理机构,SPTINR:{}", spt.getInr());
        String bchinr = spt.getBranchinr();
        if (com.brilliance.isc.common.util.StringUtils.isEmpty(bchinr)) {
            bchinr = spt.getBchkeyinr();
            if (com.brilliance.isc.common.util.StringUtils.isEmpty(bchinr)) {
                log.warn("Spt：{}记录中的branchinr和bchkeyinr都为空,返回默认全辖机构2020", spt.getInr());
                return result;
            }
        }
        Bch bch = bchService.getDirectAccBchByBchInr(bchinr);
        if (bch == null) {
            log.warn("根据SPTINR：{}获取业务所属机构的记账机构不存在,返回默认全辖机构2020", spt.getInr());
            return result;
        }
        log.info("获取处理机构,记账机构branch:{}", bch.getBranch());
        Bch directParentBch = bchService.getDirectParentBch(bch.getBranch());
        if (directParentBch == null) {
            log.warn("根据记账机构branch：{}获取上级全辖机构不存在,返回默认全辖机构2020", bch.getBranch());
            return result;
        }
        log.info("获取处理机构,全辖机构branch:{}", directParentBch.getBranch());
        return directParentBch.getBranch();
    }

    // 自动化异常通知:海运费汇出
    // errorStep 异常步骤
    public void createNotification(AutoFlow autoFlow, String errorStep){
        log.info("当前步骤为：{}",errorStep);
        if ("CPTHYF".equalsIgnoreCase(autoFlow.getSubtyp())) {
            List<Usrntf> usrntfList = new ArrayList<>();
            Bch accBch = bchService.getDirectAccBch(autoFlow.getBranch());
            Hyfcfg req = new Hyfcfg();
            req.setBranch(accBch.getBranch());
            Hyfcfg hyfcfg = hyfcfgMapper.listByBranch(req);
            if (hyfcfg != null) {
                String extkeyori = hyfcfg.getExtkeyori();
                String extkeyori02 = hyfcfg.getExtkeyori02();
                if (com.brilliance.isc.common.util.StringUtils.isNotEmpty(extkeyori)) {
                    Usrntf usrntf = createUsrntf(extkeyori, errorStep);
                    usrntfList.add(usrntf);
                }
                if (com.brilliance.isc.common.util.StringUtils.isNotEmpty(extkeyori02)) {
                    Usrntf usrntf = createUsrntf(extkeyori02, errorStep);
                    usrntfList.add(usrntf);
                }
            }
            if (usrntfList.size() > 0) {
                log.info("需要通知的用户：{}",usrntfList.toString());
                usrntfMapper.batchInsert(usrntfList);
            }
        }
    }

    private Usrntf createUsrntf(String rcvusr, String errorStep){
        Usrntf usrntf = new Usrntf();
        usrntf.setInr(counterService.dbCounterFormat("USRNTF"));
        usrntf.setSndusr("DOS");
        usrntf.setNtfsta("1");
        usrntf.setRcvusr(rcvusr);
        if (RELROW_STEP.equalsIgnoreCase(errorStep)) {
            usrntf.setNtftxt("海运费自动复核失败,请以复核角色进待办列入进行人工处理！");
        } else {
            usrntf.setNtftxt("海运费自动经办失败,请以经办角色进待办列入进行人工处理！");
        }
        usrntf.setQfsta("S");
        usrntf.setMac("0");
        return usrntf;
    }

}
