package com.friday.custody.service.trustService.guosen.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.friday.base.common.response.DubboResult;
import com.friday.custody.common.CacheAdminTrustNoTypeMap;
import com.friday.custody.common.CacheLockMap;
import com.friday.custody.common.CustodyContants;
import com.friday.custody.dto.GuosenAppSecretDTO;
import com.friday.custody.dto.TenantEscrowAuthorizationInfoDto;
import com.friday.custody.entity.*;
import com.friday.custody.service.basicService.impl.*;
import com.friday.custody.utils.*;
import com.friday.custody.enums.DataDictionaryTypeEnum;
import com.friday.custody.service.basicService.DataDictionaryTypeCertificateCsdcService;
import com.friday.custody.service.basicService.InvokeRecordService;
import com.friday.custody.service.basicService.PullDataFailureLogService;
import com.friday.custody.service.logicService.DataDictionaryBasicService;
import com.friday.custody.service.trustService.OutsourceService;
import com.friday.custody.service.trustService.guosen.common.GuosenConstants;
import com.friday.custody.service.trustService.guosen.context.ProcessGuosenDataServiceContext;
import com.friday.custody.service.trustService.guosen.dto.InvokeGuosenRecordDto;
import com.friday.custody.service.trustService.guosen.dto.RemoteDto;
import com.friday.custody.service.trustService.guosen.utils.MD5Util;
import com.friday.custody.service.trustService.htsc.common.HtscConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@Slf4j
@Service
public class OutsourceGuosenServiceImpl implements OutsourceService {
    @Value("${custody.remote.url.guosen.guosenUrlPrefix}")
    private String guosenUrlPrefix;

    private RestTemplate restTemplate = new RestTemplate();
    @Autowired
    private PullDataFailureLogService pullDataFailureLogService;
    @Autowired
    private DataDictionaryBasicService dataDictionaryBasicService;
    @Autowired
    DataDictionaryTypeCertificateCsdcService certificateCsdcService;
    @Autowired
    private FundInfoServiceImpl fundInfoService;//a
    @Autowired
    private InvestorInfoServiceImpl investorInfoService;//b
    @Autowired
    private FundNetValueInfoServiceImpl fundNetValueInfoService;//c
    @Autowired
    private CustShareInfoServiceImpl custShareInfoService;//d
    @Autowired
    private TgAccountBalanceInfoServiceImpl tgAccountBalanceInfoService;//e
    @Autowired
    private TgAccountSeqInfoServiceImpl tgAccountSeqInfoService;//f
    @Autowired
    private MjAccountBalanceInfoServiceImpl mjAccountBalanceInfoService;//g
    @Autowired
    private MjAccountSeqInfoServiceImpl mjAccountSeqInfoService;//h
    @Autowired
    private FundTradeInfoServiceImpl fundTradeInfoService;//i
    @Autowired
    private FundValueTableDataInfoServiceImpl fundValueTableDataInfoService;//j
    @Autowired
    private InvestorBankInfoServiceImpl investorBankInfoService;

    @Autowired
    private AccountInfoServiceImpl accountInfoService;
    @Resource(name = "pullDataThreadPool")
    private ExecutorService pullDataThreadPool;

    @Autowired(required = false)
    DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired(required = false)
    TransactionDefinition transactionDefinition;

    @Override
    public R initTenantData(List<TenantEscrowAuthorizationInfoDto> tenants) {
        return null;
    }

    @Override
    public R initTenantDataSingle(TenantEscrowAuthorizationInfoDto tenant) {
        log.info("国信托管数据初始化...");
        getDataFromGuosen(tenant, CustodyContants.INIT_BY_MONTH_LOOP_TIME); //先都拿1年，客户有需求再增加到3年
        log.info("国信托管数据初始化结束:");
        return R.ok("国信托管数据初始化结束！");

    }

    // //prod
//    private String urlProffix = "https://zctg.guosen.com.cn/xxl/";
//    private String appId = "wzqyzc";
//    private String secret = "wzqyzc668tt3355dt55sc";
//    private String managerCode = "6661";
    private void getDataFromGuosen(TenantEscrowAuthorizationInfoDto tenant, int loopTime) {
        String trustNo = "22";
        try {
            //1 基金产品信息表（fund_info）
            processFundInfoData(tenant, trustNo);
            //2 基金出资人信息表（investor_info）
            processInvestorInfoData(tenant, trustNo);
            //3 基金净值信息表（fund_net_value_info）
//            processFundNetValueInfoData(tenant, trustNo, loopTime);
            //4 基金持有信息表（cust_share_info）
            processCustInfoData(tenant, trustNo);
            //20 基金估值报表数据表 fund_value_table_data_info
            processFundValueTableDataInfoData(tenant, trustNo, loopTime);
            //16 募集⼾银⾏账⼾余额信息表（MJAccount_balance_info）
            processMJAccountBalanceInfoData(tenant, trustNo);
            //19 募集⼾流⽔表（MJAccount_seq_info）
            processMJAccountSeqInfoData(tenant, trustNo, loopTime);
            //15 资金账户基本信息表 account_info
            processAccountInfoData(tenant, trustNo);
            //16 托管⼾银⾏账⼾余额信息表（TGAccount_balance_info）
            processTGAccountBalanceInfoData(tenant, trustNo);
            //17 托管⼾流⽔表（TGAccount_seq_info）
            processTGAccountSeqInfoData(tenant, trustNo, loopTime);
            //8 基金交易确认信息表（fund_trade_info）
            processFundTradeInfoData(tenant, trustNo, loopTime);
            //20 基金估值报表数据表 fund_value_table_data_info
            processFundValueTableDataInfoData(tenant, trustNo, loopTime);

            //业绩报酬信息表（fund_profit_info，
            processFundProfitData(tenant, trustNo, loopTime);
            //基金持有份额变化流水表（cust_share_seq_info
            processCustShareSeqInfoData(tenant, trustNo, loopTime);
            //21基金费用数据表(fund_fee_data_info)
            processFundFeeDataInfo(tenant,trustNo,loopTime);
            //基金开放日数据表（fund_open_info）
            processFundOpenInfoData(tenant, trustNo, loopTime);

        } catch (Exception e) {
            log.error("国信托管数据拉取失败:", e);
        } finally {
            //todo tl的remove
            fundInnerCodeAndCodeTL.remove();
            accountNumTL.remove();
        }
    }
    private void getDataFromGuosenAt7Am(TenantEscrowAuthorizationInfoDto tenant, int loopTime) {
        String trustNo = "22";
        try {
            //1 基金产品信息表（fund_info）
            processFundInfoData(tenant, trustNo);
            //2 基金出资人信息表（investor_info）
            processInvestorInfoData(tenant, trustNo);
            //3 基金净值信息表（fund_net_value_info）
//            processFundNetValueInfoData(tenant, trustNo, loopTime);
            //4 基金持有信息表（cust_share_info）
            processCustInfoData(tenant, trustNo);
            //20 基金估值报表数据表 fund_value_table_data_info
            processFundValueTableDataInfoData(tenant, trustNo, loopTime);
            //15 资金账户基本信息表 account_info
            processAccountInfoData(tenant, trustNo);
            //17 托管⼾流⽔表（TGAccount_seq_info）(托管户历史流水查询)
            processTGAccountSeqInfoHistoryData(tenant, trustNo, loopTime);
            //8 基金交易确认信息表（fund_trade_info）
            processFundTradeInfoData(tenant, trustNo, loopTime);
            //20 基金估值报表数据表 fund_value_table_data_info
            processFundValueTableDataInfoData(tenant, trustNo, loopTime);

            //业绩报酬信息表（fund_profit_info，
            processFundProfitData(tenant, trustNo, loopTime);
            //基金持有份额变化流水表（cust_share_seq_info
            processCustShareSeqInfoData(tenant, trustNo, loopTime);
            //21基金费用数据表(fund_fee_data_info)
            processFundFeeDataInfo(tenant,trustNo,loopTime);
            //基金开放日信息表(fund_open_info)
            processFundOpenInfoData(tenant, trustNo, loopTime);
        } catch (Exception e) {
            log.error("国信托管数据拉取失败:", e);
        } finally {
            //todo tl的remove
            fundInnerCodeAndCodeTL.remove();
            accountNumTL.remove();
        }
    }

    private void processFundOpenInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, int loopTime) {
        try {
            //1、封装请求参数
            //2、获取远端数据
            String startDate;
            String endDate;
            LocalDate localDate = LocalDate.now();
            LocalDate emptyDate;
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            endDate = dateTimeFormatter.format(localDate);
            Map<String, String> fundInnerCodeAndCodeMap = fundInnerCodeAndCodeTL.get();
            loopTime = loopTime == CustodyContants.INIT_BY_MONTH_LOOP_TIME ? CustodyContants.INIT_BY_DAY_LOOP_TIME : CustodyContants.UPDATE_BY_DAY_DAILY_LOOP_TIME;
            int loopTimeByWeek = loopTime / 5;
            for (int i = 0; i < loopTimeByWeek; i++) {
                HashMap<String, Object> params = new HashMap<>();
                emptyDate = localDate.minusDays(5);
                startDate = dateTimeFormatter.format(emptyDate);
                String finalStartDate = startDate;
                String finalEndDate = endDate;
                params.put("startDate", finalStartDate);
                params.put("endDate", finalEndDate);
                String key = tenant.getTenantName() + "::" + trustNo + "::" + GuosenConstants.FUND_OPEN_INFO;
                InvokeGuosenRecordDto dto = InvokeGuosenRecordDto.builder().url(GuosenConstants.FUND_OPEN_INFO)
                        .key(key)
                        .tenant(tenant)
                        .params(params)
                        .trustNo(trustNo)
                        .fundInnerCodeAndCodeMap(fundInnerCodeAndCodeMap)
                        .build();
                CacheAdminTrustNoTypeMap.put(key, getReqFlag(loopTime, i), dto);
                localDate = emptyDate;
                endDate = startDate;
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processFundOpenInfo:", e);
            //todo 处理失败
        }
    }

    public void processFundFeeDataInfo(TenantEscrowAuthorizationInfoDto tenant, String trustNo, int loopTime) {
        try {
            //1、封装请求参数
            //2、获取远端数据
            String startDate;
            String endDate;
            LocalDate localDate = LocalDate.now();
            LocalDate emptyDate;
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            endDate = dateTimeFormatter.format(localDate);
            Map<String, String> fundInnerCodeAndCodeMap = fundInnerCodeAndCodeTL.get();
            loopTime = loopTime == CustodyContants.INIT_BY_MONTH_LOOP_TIME ? CustodyContants.INIT_BY_DAY_LOOP_TIME : CustodyContants.UPDATE_BY_DAY_DAILY_LOOP_TIME;
            int loopTimeByWeek = loopTime / 5;
            for (int i = 0; i < loopTimeByWeek; i++) {
                HashMap<String, Object> params = new HashMap<>();
                emptyDate = localDate.minusDays(5);
                startDate = dateTimeFormatter.format(emptyDate);
                String finalStartDate = startDate;
                String finalEndDate = endDate;
                params.put("beginDate", finalStartDate);
                params.put("endDate", finalEndDate);
                String key = tenant.getTenantName() + "::" + trustNo + "::" + GuosenConstants.EXPENSE_QUERYLIST;
                InvokeGuosenRecordDto dto = InvokeGuosenRecordDto.builder().url(GuosenConstants.EXPENSE_QUERYLIST)
                        .key(key)
                        .tenant(tenant)
                        .params(params)
                        .trustNo(trustNo)
                        .fundInnerCodeAndCodeMap(fundInnerCodeAndCodeMap)
                        .build();
                CacheAdminTrustNoTypeMap.put(key, getReqFlag(loopTime, i), dto);
                localDate = emptyDate;
                endDate = startDate;
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processFundFeeDataInfo获取数据失败！", e);
        }
    }

    private void processCustShareSeqInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, int loopTime) {
        try {
            //1、封装请求参数
            //2、获取远端数据
            String startDate;
            String endDate;
            LocalDate localDate = LocalDate.now();
//            localDate = localDate.minusDays(1);
            LocalDate emptyDate;
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            endDate = dateTimeFormatter.format(localDate);
            loopTime = loopTime == CustodyContants.INIT_BY_MONTH_LOOP_TIME ? CustodyContants.INIT_BY_DAY_LOOP_TIME : CustodyContants.UPDATE_BY_DAY_DAILY_LOOP_TIME;
            int loopTimeByWeek = loopTime / 5;
            for (int i = 0; i < loopTimeByWeek; i++) {
                HashMap<String, Object> params = new HashMap<>();
                emptyDate = localDate.minusDays(5);
                startDate = dateTimeFormatter.format(emptyDate);
                String finalStartDate = startDate;
                String finalEndDate = endDate;
                params.put("startDate", finalStartDate);
                params.put("endDate", finalEndDate);
                String key = tenant.getTenantName() + "::" + trustNo + "::" + GuosenConstants.CUSTOMER_SHARE_CURRENT_DETAIL_LIST_API;
                InvokeGuosenRecordDto dto = InvokeGuosenRecordDto.builder().url(GuosenConstants.CUSTOMER_SHARE_CURRENT_DETAIL_LIST_API)
                        .key(key)
                        .tenant(tenant)
                        .params(params)
                        .trustNo(trustNo)
                        .build();
                CacheAdminTrustNoTypeMap.put(key, getReqFlag(loopTime, i), dto);
                localDate = emptyDate;
                endDate = startDate;
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processCustShareSeqInfoData:", e);
            //todo 处理失败
        }
    }

    private void processFundProfitData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, int loopTime) {
        try {
            //1、封装请求参数
            //2、获取远端数据
            String startDate;
            String endDate;
            LocalDate localDate = LocalDate.now();
//            localDate = localDate.minusDays(1);
            LocalDate emptyDate;
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            endDate = dateTimeFormatter.format(localDate);
            Map<String, String> fundInnerCodeAndCodeMap = fundInnerCodeAndCodeTL.get();
            loopTime = loopTime == CustodyContants.INIT_BY_MONTH_LOOP_TIME ? CustodyContants.INIT_BY_DAY_LOOP_TIME : CustodyContants.UPDATE_BY_DAY_DAILY_LOOP_TIME;
            int loopTimeByWeek = loopTime / 5;
            for (int i = 0; i < loopTimeByWeek; i++) {
                HashMap<String, Object> params = new HashMap<>();
                emptyDate = localDate.minusDays(5);
                startDate = dateTimeFormatter.format(emptyDate);
                String finalStartDate = startDate;
                String finalEndDate = endDate;
                params.put("startDate", finalStartDate);
                params.put("endDate", finalEndDate);
                String key = tenant.getTenantName() + "::" + trustNo + "::" + GuosenConstants.PERSONAL_REWARD_LIST_API;
                InvokeGuosenRecordDto dto = InvokeGuosenRecordDto.builder().url(GuosenConstants.PERSONAL_REWARD_LIST_API)
                        .key(key)
                        .tenant(tenant)
                        .params(params)
                        .trustNo(trustNo)
                        .fundInnerCodeAndCodeMap(fundInnerCodeAndCodeMap)
                        .build();
                CacheAdminTrustNoTypeMap.put(key, getReqFlag(loopTime, i), dto);
                localDate = emptyDate;
                endDate = startDate;
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processFundProfitData:", e);
            //todo 处理失败
        }
    }

    private void processFundNetValueInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, int loopTime) {
//        processFundNetValueInfoData(tenant,trustNo,loopTime,"0");
//        processFundNetValueInfoData(tenant, trustNo, loopTime, "1");
    }

    private void processFundNetValueInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, int loopTime, String checkStatus) {
        try {
            //1、封装请求参数
            //2、获取远端数据
            //2.2 托管户历史流水查询接口
            String startDate;
            String endDate;
            LocalDate localDate = LocalDate.now();
//            localDate = localDate.minusDays(1);
            LocalDate emptyDate;
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            endDate = dateTimeFormatter.format(localDate);
            Map<String, String> fundInnerCodeAndCodeMap = fundInnerCodeAndCodeTL.get();
            loopTime = loopTime == CustodyContants.INIT_BY_MONTH_LOOP_TIME ? CustodyContants.INIT_BY_DAY_LOOP_TIME : CustodyContants.UPDATE_BY_DAY_DAILY_LOOP_TIME;
            int loopTimeByWeek = loopTime / 5;
            for (int i = 0; i < loopTimeByWeek; i++) {
                HashMap<String, Object> params = new HashMap<>();
                emptyDate = localDate.minusDays(5);
                startDate = dateTimeFormatter.format(emptyDate);
                String finalStartDate = startDate;
                String finalEndDate = endDate;
                params.put("checkStatus", checkStatus);
                params.put("startDate", finalStartDate);
                params.put("endDate", finalEndDate);
                String key = tenant.getTenantName() + "::" + trustNo + "::" + GuosenConstants.NET_VALUE_INFO_LIST_API;
                InvokeGuosenRecordDto dto = InvokeGuosenRecordDto.builder().url(GuosenConstants.NET_VALUE_INFO_LIST_API)
                        .key(key)
                        .tenant(tenant)
                        .params(params)
                        .trustNo(trustNo)
                        .fundInnerCodeAndCodeMap(fundInnerCodeAndCodeMap)
                        .build();
                CacheAdminTrustNoTypeMap.put(key, getReqFlag(loopTime, i), dto);
                localDate = emptyDate;
                endDate = startDate;
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processFundNetValueInfoData:", e);
            //todo 处理失败
        }
    }

    @Autowired
    ProcessGuosenDataServiceContext processGuosenDataServiceContext;

    private void executeQuery(InvokeGuosenRecordDto dto) {
        if (Objects.isNull(dto)) return;
        CompletableFuture.supplyAsync(() -> {
            ReentrantLock lock = CacheLockMap.get(dto.getKey());
            if (!lock.tryLock()){
                CacheAdminTrustNoTypeMap.put(dto.getKey(), CustodyContants.UPDATE_FLAG, dto);
                return null;
            }
            try {
                List<Object> remoteDatas = getRes(dto.getUrl(), dto.getTenant(), dto.getParams(), dto.getTrustNo(), dto);
                dto.setRemoteDatas(remoteDatas);
                if (GuosenConstants.VALUATION_LIST_V2_API.equals(dto.getUrl())) {
                    dto.setNetValueRemoteDatas(executeNetValueQuery(dto));
                }
                processGuosenDataServiceContext.processData(dto);
            } catch (Exception e) {
                log.error("失败！" + dto.getKey(), e);
            } finally {
                lock.unlock();
            }
            // TODO: 2022/1/13 加一个处理失败的日志统计表
            return null;
        }, pullDataThreadPool);
    }

    private List<Object> executeNetValueQuery(InvokeGuosenRecordDto dto) {
        //如果报错就塞入空值吧
        List<Object> result = null;
        HashMap<String, Object> originParam = dto.getParams();
        String key = dto.getTenant().getTenantName() + "::" + dto.getTrustNo() + "::" + GuosenConstants.NET_VALUE_INFO_LIST_API;
        ReentrantLock lock = CacheLockMap.get(key);
        try {
            if (!lock.tryLock()){
                log.error("NET_VALUE_INFO_LIST_API 加锁失败： the key is : {} ;the time is {} ",key,originParam.get("valuationDate"));
                return null;
            }
            HashMap<String, Object> params = new HashMap<>();
            params.put("checkStatus", originParam.get("checkStatus"));
            params.put("startDate", originParam.get("valuationDate"));
            params.put("endDate", originParam.get("valuationDate"));
            return getRes(GuosenConstants.NET_VALUE_INFO_LIST_API, dto.getTenant(), params, dto.getTrustNo(), null);
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processFundNetValueInfoData:", e);
            //todo 处理失败
            return null;
        }finally {
            lock.unlock();
        }
    }


    public void processFundValueTableDataInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, int loopTime) {
//        processFundValueTableDataInfoData(tenant,trustNo,loopTime,"0");
        processFundValueTableDataInfoData(tenant, trustNo, loopTime, "1");//查1-已复核，只要这个
    }

    private void processFundValueTableDataInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, int loopTime, String checkStatus) {
        try {
            //1、封装请求参数
            //2、获取远端数据
            //2.2 托管户历史流水查询接口
            String startDate;
            LocalDate localDate = LocalDate.now();
//            localDate = localDate.minusDays(1);
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            Map<String, String> fundInnerCodeAndCodeMap = fundInnerCodeAndCodeTL.get();
            loopTime = loopTime == CustodyContants.INIT_BY_MONTH_LOOP_TIME ? CustodyContants.INIT_BY_DAY_LOOP_TIME : CustodyContants.UPDATE_BY_DAY_DAILY_LOOP_TIME;
            for (int i = 0; i < loopTime; i++) {
                HashMap<String, Object> params = new HashMap<>();
                startDate = dateTimeFormatter.format(localDate);
                localDate = localDate.minusDays(1);
                String finalStartDate = startDate;
                // 去除周末
                if (CalanderUtil.isWeekend(finalStartDate)) continue;
                //todo  这个时间没有进入循环
                params.put("valuationDate", finalStartDate);
                params.put("checkStatus", checkStatus);
                String key = tenant.getTenantName() + "::" + trustNo + "::" + GuosenConstants.VALUATION_LIST_V2_API;
                InvokeGuosenRecordDto dto = InvokeGuosenRecordDto.builder().url(GuosenConstants.VALUATION_LIST_V2_API)
                        .key(key)
                        .tenant(tenant)
                        .params(params)
                        .trustNo(trustNo)
                        .fundInnerCodeAndCodeMap(fundInnerCodeAndCodeMap)
                        .build();
                CacheAdminTrustNoTypeMap.put(key, getReqFlag(loopTime, i), dto);
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processFundValueTableDataInfoData:", e);
            //todo 处理失败
        }
    }


    private void processFundTradeInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, int loopTime) {
        try {
            //1、封装请求参数
            //2、获取远端数据
            //2.2 托管户历史流水查询接口
            String startDate;
            String endDate;
            LocalDate localDate = LocalDate.now();
            localDate = localDate.minusDays(1);
            LocalDate lastMonth;
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            endDate = dateTimeFormatter.format(localDate);
            Map<String, String> fundInnerCodeAndCodeMap = fundInnerCodeAndCodeTL.get();
            loopTime = loopTime == CustodyContants.INIT_BY_MONTH_LOOP_TIME ? CustodyContants.INIT_BY_DAY_LOOP_TIME : CustodyContants.UPDATE_BY_DAY_DAILY_LOOP_TIME;
            int loopTimeByWeek = loopTime / 5;
            for (int i = 0; i < loopTimeByWeek; i++) {
                HashMap<String, Object> params = new HashMap<>();
                lastMonth = localDate.minusDays(5);
                startDate = dateTimeFormatter.format(lastMonth);
                String finalStartDate = startDate;
                String finalEndDate = endDate;
                params.put("endDate", finalEndDate);
                params.put("startDate", finalStartDate);
                String key = tenant.getTenantName() + "::" + trustNo + "::" + GuosenConstants.TA_CONFIRM_INFO_LIST_API;
                InvokeGuosenRecordDto dto = InvokeGuosenRecordDto.builder().url(GuosenConstants.TA_CONFIRM_INFO_LIST_API)
                        .key(key)
                        .tenant(tenant)
                        .params(params)
                        .trustNo(trustNo)
                        .fundInnerCodeAndCodeMap(fundInnerCodeAndCodeMap)
                        .build();
                CacheAdminTrustNoTypeMap.put(key, getReqFlagByWeek(loopTime, i), dto);
                localDate = lastMonth;
                endDate = startDate;
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processFundTradeInfoData:", e);
            //todo 处理失败
        }
    }


    private void processTGAccountSeqInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, int loopTime) {
        try {
            //1、封装请求参数
            HashMap<String, Object> params = new HashMap<>();
            List<String> accountNums = accountNumTL.get();
            if (CollectionUtils.isEmpty(accountNums)) {
                log.error("processTGAccountSeqInfoData the accountNums is empty, the tenant is {},the trustNo is {}", JSON.toJSONString(tenant), trustNo);
                return;
            }
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < accountNums.size(); i++) {
                if (i != 0) {
                    sb.append(",");
                }
                sb.append(accountNums.get(i));
            }
            params.put("account", sb.toString());
            //2、获取远端数据
            //2.1 托管户实时流水查询接口
            Map<String, String> fundInnerCodeAndCodeMap = fundInnerCodeAndCodeTL.get();
            List<Object> remoteDatas = getRes(GuosenConstants.ACCOUNT_DETAIL_LIST_V2_API, tenant, params, trustNo, null);
            if (!CollectionUtils.isEmpty(remoteDatas)) {
                //3、封装成标准表的对象
                List<TgAccountSeqInfoEntity> entities = getTGAccountSeqInfoData(tenant, remoteDatas, trustNo, fundInnerCodeAndCodeMap);
                //4、调用 basicservice 的保存数据接口
                tgAccountSeqInfoService.saveData(null, entities);
            }
            //2.2 托管户历史流水查询接口
            String startDate;
            String endDate;
            LocalDate localDate = LocalDate.now();
            LocalDate lastMonth;
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            endDate = dateTimeFormatter.format(localDate);
            loopTime = loopTime == CustodyContants.INIT_BY_MONTH_LOOP_TIME ? CustodyContants.INIT_BY_DAY_LOOP_TIME : CustodyContants.UPDATE_BY_DAY_DAILY_LOOP_TIME;
            int loopTimeByWeek = loopTime / 5;
            for (int i = 0; i < loopTimeByWeek; i++) {
                HashMap<String, Object> params2 = new HashMap<>();
                lastMonth = localDate.minusDays(5);
                startDate = dateTimeFormatter.format(lastMonth);
                String finalStartDate = startDate;
                String finalEndDate = endDate;
                params2.put("startTime", finalStartDate);
                params2.put("endTime", finalEndDate);
                String key = tenant.getTenantName() + "::" + trustNo + "::" + GuosenConstants.ACCOUNT_HISTORY_DETAIL_LIST_V2_API;
                InvokeGuosenRecordDto dto = InvokeGuosenRecordDto.builder().url(GuosenConstants.ACCOUNT_HISTORY_DETAIL_LIST_V2_API)
                        .key(key)
                        .tenant(tenant)
                        .params(params2)
                        .trustNo(trustNo)
                        .fundInnerCodeAndCodeMap(fundInnerCodeAndCodeMap)
                        .build();
                CacheAdminTrustNoTypeMap.put(key, getReqFlagByWeek(loopTime, i), dto);
                localDate = lastMonth;
                endDate = startDate;
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processTGAccountSeqInfoData:", e);
            //todo 处理失败
//            pullDataFailureLogService.saveErrorData(tenant.getTenantName(),  PullDataFailureUtil.createErrorMap(trustNo, GuosenConstants.AMAC_CODE_QUERY_LIST_API, null, "appId=" + tenant.getAppKey(), e.getMessage()));
        }

    }
    private void processTGAccountSeqInfoCurrentData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, int loopTime) {
        try {
            //1、封装请求参数
            HashMap<String, Object> params = new HashMap<>();
            List<String> accountNums = accountNumTL.get();
            if (CollectionUtils.isEmpty(accountNums)) {
                log.error("processTGAccountSeqInfoData the accountNums is empty, the tenant is {},the trustNo is {}", JSON.toJSONString(tenant), trustNo);
                return;
            }
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < accountNums.size(); i++) {
                if (i != 0) {
                    sb.append(",");
                }
                sb.append(accountNums.get(i));
            }
            params.put("account", sb.toString());
            //2、获取远端数据
            //2.1 托管户实时流水查询接口
            Map<String, String> fundInnerCodeAndCodeMap = fundInnerCodeAndCodeTL.get();
            List<Object> remoteDatas = getRes(GuosenConstants.ACCOUNT_DETAIL_LIST_V2_API, tenant, params, trustNo, null);
            if (!CollectionUtils.isEmpty(remoteDatas)) {
                //3、封装成标准表的对象
                List<TgAccountSeqInfoEntity> entities = getTGAccountSeqInfoData(tenant, remoteDatas, trustNo, fundInnerCodeAndCodeMap);
                //4、调用 basicservice 的保存数据接口
                tgAccountSeqInfoService.saveData(null, entities);
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processTGAccountSeqInfoData:", e);
            //todo 处理失败
//            pullDataFailureLogService.saveErrorData(tenant.getTenantName(),  PullDataFailureUtil.createErrorMap(trustNo, GuosenConstants.AMAC_CODE_QUERY_LIST_API, null, "appId=" + tenant.getAppKey(), e.getMessage()));
        }

    }
    private void processTGAccountSeqInfoHistoryData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, int loopTime) {
        try {
            //1、封装请求参数
            HashMap<String, Object> params = new HashMap<>();
            List<String> accountNums = accountNumTL.get();
            if (CollectionUtils.isEmpty(accountNums)) {
                log.error("processTGAccountSeqInfoData the accountNums is empty, the tenant is {},the trustNo is {}", JSON.toJSONString(tenant), trustNo);
                return;
            }
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < accountNums.size(); i++) {
                if (i != 0) {
                    sb.append(",");
                }
                sb.append(accountNums.get(i));
            }
            params.put("account", sb.toString());
            Map<String, String> fundInnerCodeAndCodeMap = fundInnerCodeAndCodeTL.get();
            //2、获取远端数据
            //2.2 托管户历史流水查询接口
            String startDate;
            String endDate;
            LocalDate localDate = LocalDate.now();
            LocalDate lastMonth;
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            endDate = dateTimeFormatter.format(localDate);
            loopTime = loopTime == CustodyContants.INIT_BY_MONTH_LOOP_TIME ? CustodyContants.INIT_BY_DAY_LOOP_TIME : CustodyContants.UPDATE_BY_DAY_DAILY_LOOP_TIME;
            int loopTimeByWeek = loopTime / 5;
            for (int i = 0; i < loopTimeByWeek; i++) {
                HashMap<String, Object> params2 = new HashMap<>();
                lastMonth = localDate.minusDays(5);
                startDate = dateTimeFormatter.format(lastMonth);
                String finalStartDate = startDate;
                String finalEndDate = endDate;
                params2.put("startTime", finalStartDate);
                params2.put("endTime", finalEndDate);
                String key = tenant.getTenantName() + "::" + trustNo + "::" + GuosenConstants.ACCOUNT_HISTORY_DETAIL_LIST_V2_API;
                InvokeGuosenRecordDto dto = InvokeGuosenRecordDto.builder().url(GuosenConstants.ACCOUNT_HISTORY_DETAIL_LIST_V2_API)
                        .key(key)
                        .tenant(tenant)
                        .params(params2)
                        .trustNo(trustNo)
                        .fundInnerCodeAndCodeMap(fundInnerCodeAndCodeMap)
                        .build();
                CacheAdminTrustNoTypeMap.put(key, getReqFlagByWeek(loopTime, i), dto);
                localDate = lastMonth;
                endDate = startDate;
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processTGAccountSeqInfoData:", e);
            //todo 处理失败
//            pullDataFailureLogService.saveErrorData(tenant.getTenantName(),  PullDataFailureUtil.createErrorMap(trustNo, GuosenConstants.AMAC_CODE_QUERY_LIST_API, null, "appId=" + tenant.getAppKey(), e.getMessage()));
        }

    }

    private List<TgAccountSeqInfoEntity> getTGAccountSeqInfoData(TenantEscrowAuthorizationInfoDto tenant, List<Object> remoteDatas, String trustNo, Map<String, String> fundInnerCodeAndCodeMap) {
        //1、获取字典表数据
        Map<String, String> paymentDirectionDic = dataDictionaryBasicService.queryWithDetail(DataDictionaryTypeEnum.PAYMENT_DIRECTION_SERVICE, trustNo);
        //2、封装数据
        List<TgAccountSeqInfoEntity> result = new ArrayList<>();
        HashMap<String, String> remoteDataMap;
        Date now = new Date();
        for (Object remoteData : remoteDatas) {
            remoteDataMap = (HashMap<String, String>) remoteData;
            String fundCode = fundInfoService.getFundCodeByInnerFundCode(remoteDataMap.get("fundCode"),trustNo);
            String paymentTime = remoteDataMap.get("paymentTime");
            String transferDate = null;
            String transferTime = null;
            //1 todo transferDate 格式错误
            if (StringUtils.isNotBlank(paymentTime)) {
                String[] split = paymentTime.split(" ");
                transferDate = split[0].replace("-", "");
                transferTime = split[1].split("\\.")[0];
            }
            result.add(TgAccountSeqInfoEntity.builder()
                    .innerFundCode(remoteDataMap.get("fundCode"))
                    .fundCode(remoteDataMap.get("fundCode"))
                    .fundName(remoteDataMap.get("fundName"))
                    .opBankName(remoteDataMap.get("otherPartyAccountBank"))
                    .opAccountName(remoteDataMap.get("otherPartyAccountName"))
                    .opAccount(remoteDataMap.get("otherPartyAccountNumber"))
                    .transferInOut(MatchDataDictionaryKeyUtil.getKey(paymentDirectionDic, remoteDataMap.get("paymentDirection")))
                    .amount(BigDecimalUtil.obj2BigDecimal(JSON.toJSONString(remoteDataMap.get("paymentMoney"))))
                    .balance(BigDecimalUtil.obj2BigDecimal(JSON.toJSONString(remoteDataMap.get("balance"))))
                    .transferDate(transferDate)
                    .transferTime(transferTime)
                    .summary(remoteDataMap.get("paymentRemark"))
                    .memo(remoteDataMap.get("paymentUse"))
                    .bankName(remoteDataMap.get("thePartyAccountBank"))
                    .accountName(remoteDataMap.get("thePartyAccountName"))
                    .account(remoteDataMap.get("thePartyAccountNumber"))
                    .updateDate(DateFormaterUtil.removeZero(remoteDataMap.get("updateTime")))
                    .trustNo(trustNo)
                    .admin(tenant.getTenantName())
                    .createTime(now)
                    .updateTime(now)
                    .build());
        }
        return result;


    }


    private void processMJAccountSeqInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, int loopTime) {
        try {
            loopTime = loopTime == CustodyContants.INIT_BY_MONTH_LOOP_TIME ? CustodyContants.INIT_BY_DAY_LOOP_TIME : CustodyContants.UPDATE_BY_DAY_DAILY_LOOP_TIME;
            String startDate;
            LocalDate localDate = LocalDate.now();
            LocalDate lastDay;
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

            for (int i = 0; i < loopTime; i++) {
                lastDay = localDate.minusDays(1);
                startDate = dateTimeFormatter.format(lastDay);
                // 去除周末
                if (CalanderUtil.isWeekend(startDate)) continue;
                String finalStartDate = startDate + " 00:00:00";
                String finalEndDate = startDate + " 21:59:59";
                //1、封装请求参数
                HashMap<String, Object> params = new HashMap<>();
                params.put("startTime", finalStartDate);
                params.put("endTime", finalEndDate);
                String key = tenant.getTenantName() + "::" + trustNo + "::" + GuosenConstants.TURN_OVER_LIST_V2_API;
                InvokeGuosenRecordDto dto = InvokeGuosenRecordDto.builder().url(GuosenConstants.TURN_OVER_LIST_V2_API)
                        .key(key)
                        .tenant(tenant)
                        .params(params)
                        .trustNo(trustNo)
                        .build();
                CacheAdminTrustNoTypeMap.put(key, getReqFlag(loopTime, i), dto);
                localDate = lastDay;
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processMJAccountSeqInfoData:", e);
            //todo 处理失败
//            pullDataFailureLogService.saveErrorData(tenant.getTenantName(),  PullDataFailureUtil.createErrorMap(trustNo, GuosenConstants.AMAC_CODE_QUERY_LIST_API, null, "appId=" + tenant.getAppKey(), e.getMessage()));
        }
    }

    private int getReqFlag(int loopTime, int i) {
        int reqFlag = CustodyContants.UPDATE_FLAG;
        if (loopTime == CustodyContants.INIT_BY_DAY_LOOP_TIME || i > CustodyContants.UPDATE_BY_DAY_DAILY_LOOP_TIME)
            reqFlag = CustodyContants.INIT_FLAG;
        return reqFlag;
    }

    private int getReqFlagByWeek(int loopTime, int i) {
        int reqFlag = CustodyContants.UPDATE_FLAG;
        if (loopTime == CustodyContants.INIT_BY_DAY_LOOP_TIME || i > 2) reqFlag = CustodyContants.INIT_FLAG;
        return reqFlag;
    }

    private void processTGAccountBalanceInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo) {
        try {
            //1、封装请求参数
            HashMap<String, Object> params = new HashMap<>();
            List<String> accountNums = accountNumTL.get();
            if (CollectionUtils.isEmpty(accountNums)) {
                log.error("processTGAccountBalanceInfoData the accountNums is empty, the tenant is {},the trustNo is {}", JSON.toJSONString(tenant), trustNo);
                return;
            }
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < accountNums.size(); i++) {
                if (i != 0) {
                    sb.append(",");
                }
                sb.append(accountNums.get(i));
            }
            params.put("account", sb.toString());
            //2、获取远端数据
            List<Object> remoteDatas = getRes(GuosenConstants.ACCOUNT_LIST_V2_API, tenant, params, trustNo, null);
            if (!CollectionUtils.isEmpty(remoteDatas)) {
                //3、封装成标准表的对象
                List<TgAccountBalanceInfoEntity> entities = getTGAccountBalanceInfoData(tenant, remoteDatas, trustNo);
                //4、调用 basicservice 的保存数据接口
                tgAccountBalanceInfoService.saveData(null, entities);
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processTGAccountBalanceInfoData:", e);
            //todo 处理失败
//            pullDataFailureLogService.saveErrorData(tenant.getTenantName(),  PullDataFailureUtil.createErrorMap(trustNo, GuosenConstants.AMAC_CODE_QUERY_LIST_API, null, "appId=" + tenant.getAppKey(), e.getMessage()));
        }
    }

    private List<TgAccountBalanceInfoEntity> getTGAccountBalanceInfoData(TenantEscrowAuthorizationInfoDto tenant, List<Object> remoteDatas, String trustNo) {

        //1、获取字典表数据
        //2、封装数据
        List<TgAccountBalanceInfoEntity> result = new ArrayList<>();
        HashMap<String, String> remoteDataMap;
        Date now = new Date();
        for (Object remoteData : remoteDatas) {
            remoteDataMap = (HashMap<String, String>) remoteData;
            result.add(TgAccountBalanceInfoEntity.builder()
                    .bankAcco(remoteDataMap.get("accountNumber"))
                    .accoBalance(BigDecimalUtil.obj2BigDecimal(remoteDataMap.get("balance")))
                    //2022-03-14 20:26:23
                    .updateDate(DateFormaterUtil.removeZero(remoteDataMap.get("updateTime")))
                    .trustNo(trustNo)
                    .admin(tenant.getTenantName())
                    .createTime(now)
                    .updateTime(now)
                    .build());
        }
        return result;
    }

    private static final ThreadLocal<List<String>> accountNumTL = new ThreadLocal<>();

    private void processAccountInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo) {
        try {
            //1、封装请求参数
            HashMap<String, Object> params = new HashMap<>();
            //2、获取远端数据
            List<Object> remoteDatas = getRes(GuosenConstants.GET_ACCOUNTS_API, tenant, params, trustNo, null);
            if (!ObjectUtils.isEmpty(remoteDatas)) {
                //3、封装成标准表的对象
                List<AccountInfoEntity> entities = getTGAccountInfoData(tenant, remoteDatas, trustNo);
                //4、调用 basicservice 的保存数据接口
                accountInfoService.saveData(null, entities);

                ArrayList<FundInfoEntity> fundInfoEntities = getFundInfoDataByTG(tenant, remoteDatas, trustNo);
                //4、调用 basicservice 的保存数据接口
                fundInfoService.updateStatus(fundInfoEntities);
            }

        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processAccountInfoData:", e);
            //todo 处理失败
//            pullDataFailureLogService.saveErrorData(tenant.getTenantName(),  PullDataFailureUtil.createErrorMap(trustNo, GuosenConstants.AMAC_CODE_QUERY_LIST_API, null, "appId=" + tenant.getAppKey(), e.getMessage()));
        }


    }

    private ArrayList<FundInfoEntity> getFundInfoDataByTG(TenantEscrowAuthorizationInfoDto tenant, List<Object> remoteDatas, String trustNo) {
        Map<String, String> productStatusDic = dataDictionaryBasicService.queryWithDetail(DataDictionaryTypeEnum.PRODUCT_STATUS_SERVICE, trustNo);

        ArrayList<FundInfoEntity> fundInfoEntities = new ArrayList<>();
        ArrayList<String> accountNums = new ArrayList<>();
        if (!CollectionUtils.isEmpty(remoteDatas)) {
            HashMap<String, String> remoteDataMap;
            for (Object remoteData : remoteDatas) {
                remoteDataMap = (HashMap<String, String>) remoteData;
                String fundCode = fundInfoService.getFundCodeByInnerFundCode(remoteDataMap.get("productCode"), trustNo);
                Date now = new Date();
                fundInfoEntities.add(FundInfoEntity.builder()
                        .innerFundCode(remoteDataMap.get("productCode"))
                        .fundName(remoteDataMap.get("productName"))
                        .fundStatus(MatchDataDictionaryKeyUtil.getKey(productStatusDic,remoteDataMap.get("productStatus")))
                        .fundCode(fundCode)
                        .trustNo(trustNo)
                        .admin(tenant.getTenantName())
                        .createTime(now)
                        .updateTime(now)
                        .build());
                accountNums.add(remoteDataMap.get("accountNum"));
            }
            accountNumTL.set(accountNums);
        }
        return fundInfoEntities;
    }


    private List<AccountInfoEntity> getTGAccountInfoData(TenantEscrowAuthorizationInfoDto tenant, List<Object> remoteDatas, String trustNo) {
        //1、获取字典表数据
        Map<String, String> accountStatusDic = dataDictionaryBasicService.queryWithDetail(DataDictionaryTypeEnum.ACCOUNT_STATUS_SERVICE, trustNo);
        Map<String, String> accountTypeDic = dataDictionaryBasicService.queryWithDetail(DataDictionaryTypeEnum.ACCOUNT_TYPE_SERVICE, trustNo);
        //2、封装数据
        List<AccountInfoEntity> result = new ArrayList<>();
        HashMap<String, String> remoteDataMap;
        Date now = new Date();
        for (Object remoteData : remoteDatas) {
            remoteDataMap = (HashMap<String, String>) remoteData;
            //String fundCode = fundInfoService.getFundCodeByInnerFundCode(remoteDataMap.get("productCode"),trustNo);
            result.add(AccountInfoEntity.builder()
                    .nameInBank(remoteDataMap.get("accountName"))
                    .bankAcco(remoteDataMap.get("accountNum"))
                    .branchBankName(remoteDataMap.get("bankName"))
                    .largePayAcco(remoteDataMap.get("highValueAccount"))
                    .innerFundCode(remoteDataMap.get("productCode"))
                    .fundName(remoteDataMap.get("productName"))
                    .acctStatus(MatchDataDictionaryKeyUtil.getKey(accountStatusDic, remoteDataMap.get("accountStatus")))
                    .accoTypeCode(MatchDataDictionaryKeyUtil.getKey(accountTypeDic, remoteDataMap.get("accountType")))
                    .fundCode(remoteDataMap.get("productCode"))
                    .trustNo(trustNo)
                    .admin(tenant.getTenantName())
                    .remark("tg")
                    .createTime(now)
                    .updateTime(now)
                    .build());
        }
        return result;

    }

    private void processMJAccountBalanceInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo) {
        try {
            //1、封装请求参数
            HashMap<String, Object> params = new HashMap<>();

            //2、获取远端数据
            List<Object> remoteDatas = getRes(GuosenConstants.RAISE_BALANCE_LIST_API, tenant, params, trustNo, null);
            if (!CollectionUtils.isEmpty(remoteDatas)) {
                //3、封装成标准表的对象
                List<MjAccountBalanceInfoEntity> entities = getMJAccountBalanceInfoData(tenant, remoteDatas, trustNo);
                //4、调用 basicservice 的保存数据接口
                mjAccountBalanceInfoService.saveData(null, entities);
                //5、此接口还会将数据插入accountInfo表
                List<AccountInfoEntity> accountInfoEntities = getAccountInfoFromMJData(tenant, remoteDatas, trustNo);
                accountInfoService.saveData(null, accountInfoEntities);
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processMJAccountBalanceInfoData:", e);
            //todo 处理失败
//            pullDataFailureLogService.saveErrorData(tenant.getTenantName(),  PullDataFailureUtil.createErrorMap(trustNo, GuosenConstants.AMAC_CODE_QUERY_LIST_API, null, "appId=" + tenant.getAppKey(), e.getMessage()));
        }

    }

    private List<AccountInfoEntity> getAccountInfoFromMJData(TenantEscrowAuthorizationInfoDto tenant, List<Object> remoteDatas, String trustNo) {
        //1、获取字典表数据
        // 账户类型
        Map<String, String> accountTypeDic = dataDictionaryBasicService.queryWithDetail(DataDictionaryTypeEnum.ACCOUNT_TYPE_SERVICE, trustNo);
        //2、封装数据
        List<AccountInfoEntity> result = new ArrayList<>();
        HashMap<String, String> remoteDataMap;
        Date now = new Date();
        for (Object remoteData : remoteDatas) {
            remoteDataMap = (HashMap<String, String>) remoteData;
            String fundCode = fundInfoService.getFundCodeByInnerFundCode(remoteDataMap.get("fundCode"),trustNo);
            result.add(AccountInfoEntity.builder()
                    .innerFundCode(remoteDataMap.get("fundCode"))
                    .fundName(remoteDataMap.get("fundName"))
                    .accoTypeCode(MatchDataDictionaryKeyUtil.getKey(accountTypeDic, remoteDataMap.get("accountType")))
                    .nameInBank(remoteDataMap.get("accountName"))
                    .bankAcco(remoteDataMap.get("accountNumber"))
                    .branchBankName(remoteDataMap.get("accountBank"))
                    .fundCode(fundCode)
                    .trustNo(trustNo)
                    .admin(tenant.getTenantName())
                    .remark("mj")
                    .createTime(now)
                    .updateTime(now)
                    .build());
        }
        return result;

    }

    private List<MjAccountBalanceInfoEntity> getMJAccountBalanceInfoData(TenantEscrowAuthorizationInfoDto tenant, List<Object> remoteDatas, String trustNo) {
        //1、获取字典表数据
        // 账户类型
        Map<String, String> accountTypeDic = dataDictionaryBasicService.queryWithDetail(DataDictionaryTypeEnum.ACCOUNT_TYPE_SERVICE, trustNo);
        //2、封装数据
        List<MjAccountBalanceInfoEntity> result = new ArrayList<>();
        HashMap<String, String> remoteDataMap;
        Date now = new Date();
        for (Object remoteData : remoteDatas) {
            remoteDataMap = (HashMap<String, String>) remoteData;
            result.add(MjAccountBalanceInfoEntity.builder()
                    .updateDate(DateFormaterUtil.formatterDateTime(remoteDataMap.get("updateTime"),"yyyyMMddHHmmss","yyyy-MM-dd HH:mm:ss"))
                    .accoTypeCode(MatchDataDictionaryKeyUtil.getKey(accountTypeDic, remoteDataMap.get("accountType")))
                    .bankAcco(remoteDataMap.get("accountNumber"))
                    .accoBalance(BigDecimalUtil.obj2BigDecimal(remoteDataMap.get("balance")))
                    .trustNo(trustNo)
                    .admin(tenant.getTenantName())
                    .createTime(now)
                    .updateTime(now)
                    .build());
        }
        return result;
    }

    private void processCustInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo) {
        processCustInfoData(tenant, trustNo, "0");
//        try {
//            Thread.sleep(10 * 60 * 1000);
//        } catch (InterruptedException e) {
//            log.error("processCustInfoData: Thread.sleep(); 被打断...", e);
//
//        }
//        processCustInfoData(tenant, trustNo, "1");
    }

    private void processCustInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, String isShowShareNull) {
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(transactionDefinition);
        try {
            //3.15 投资者信息查询接口全量
            //1、封装请求参数
            HashMap<String, Object> params = new HashMap<>();
            params.put("isShowShareNull", isShowShareNull);
            //2、获取远端数据
            List<Object> remoteDatas = getRes(GuosenConstants.FUNDSHARE_QUERY_SHARE_DETAIL_API, tenant, params, trustNo, null);
            if (!CollectionUtils.isEmpty(remoteDatas)) {
                //3、封装成标准表的对象 上游数据
                List<CustShareInfoEntity> entities = getCustInfoData(tenant, remoteDatas, trustNo);
                //上游数据组合主键List
                List<String> uniqueKeyListNew = entities.stream().map(entity -> entity.getAdmin() + entity.getFundCode() + entity.getFundName()
                        + entity.getFundAcco() + entity.getTradeAcco()).collect(Collectors.toList());

                //查询出当前托管、管理人下的已存在的数据
                List<CustShareInfoEntity> custShareInfoEntities = custShareInfoService.queryByTenant(tenant);
                //根据组合主键筛选出现有数据中不在上游数据范围内的数据
                List<CustShareInfoEntity> toUpdateZeroList = custShareInfoEntities.stream().filter(entity -> {
                    if (!uniqueKeyListNew.contains(entity.getUniqueKey())) {
                        return true;
                    }
                    return false;
                }).collect(Collectors.toList());
                custShareInfoService.updateDataToZero(toUpdateZeroList);
                //4、调用 basicservice 的保存数据接口
                custShareInfoService.saveData(null, entities);
            }
            dataSourceTransactionManager.commit(transaction);
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processCustInfoData:", e);
            //todo 处理失败
            dataSourceTransactionManager.rollback(transaction);
        }

    }

    private List<CustShareInfoEntity> getCustInfoData(TenantEscrowAuthorizationInfoDto tenant, List<Object> remoteDatas, String trustNo) {
        //1、获取字典表数据
        Map<String, String> sellerDic = dataDictionaryBasicService.queryWithDetail(DataDictionaryTypeEnum.SELLER_SERVICE, "国信证券");
        //2、封装数据
        List<CustShareInfoEntity> result = new ArrayList<>();
        HashMap<String, String> remoteDataMap;
        Date now = new Date();
        for (Object remoteData : remoteDatas) {
            remoteDataMap = (HashMap<String, String>) remoteData;
            String fundCode = fundInfoService.getFundCodeByInnerFundCode(remoteDataMap.get("structuredFundCode"), trustNo);
            String updateDate = DateFormaterUtil.formatterDateTime(StringUtils.isNotBlank(remoteDataMap.get("shareDate")) ? remoteDataMap.get("shareDate") + "000000" : null, CustodyContants.DATE_SHORT_DETAIL_FORMAT, CustodyContants.DATE_SHORT_ACROSS_FORMAT);
//            BigDecimal shares = fundNetValueInfoService.getSharesByUniqueKey(FundNetValueInfoEntity.builder().uniqueKey(tenant.getTenantName() + fundCode +remoteDataMap.get("structuredFundName")   + "20220420"/*updateDate*/).build());
            BigDecimal shares = fundNetValueInfoService.getSharesByFundInfo(FundNetValueInfoEntity.builder().admin(tenant.getTenantName()).fundCode(fundCode).fundName(remoteDataMap.get("structuredFundName")).build());
            result.add(CustShareInfoEntity.builder()
                    .agencyName(remoteDataMap.get("agencyName"))
                    .agencyNo(MatchDataDictionaryKeyUtil.getKey(sellerDic,remoteDataMap.get("agencyNumber")))
                    .frozenShares(BigDecimalUtil.obj2BigDecimal(remoteDataMap.get("frozenShares")))
                    .fundAcco(remoteDataMap.get("fundAccount"))
                    .lastHoldShares(BigDecimalUtil.obj2BigDecimal(remoteDataMap.get("realShares")))
                    .enableShares(new BigDecimal(remoteDataMap.get("realShares").replace(",","")).subtract(new BigDecimal(remoteDataMap.get("frozenShares").replace(",",""))))
                    //（1.根据此数据的基金备案代码fund_code、基金名称fund_name、净值日期net_value_date，在fund_net_value_info中找到匹配的记录，获取记录中的资产份额shares；
                    //2.用此数据的最新持有份额数last_hold_shares除以shares乘以100
                    //得到此字段的数据）需将String类型数据转换为Decimal(8,4)
                    // entity.setUniqueKey(entity.getAdmin() + entity.getFundCode() + entity.getFundName()  entity.getNetValueDate()); tenant.getTenantName() + remoteDataMap.get("fundCode") + remoteDataMap.get("fundName") + remoteDataMap.get("netValueDate")
                    .shareRatio(BigDecimalUtil.getShareRatio(shares ,BigDecimalUtil.obj2BigDecimal(BigDecimalUtil.obj2BigDecimal(remoteDataMap.get("realShares")))))
                    .updateDate(updateDate)
                    .innerFundCode(remoteDataMap.get("structuredFundCode"))
                    .fundName(remoteDataMap.get("structuredFundName"))
                    .tradeAcco(remoteDataMap.get("tradeAccount"))
                    .fundCode(fundCode)//此字段的数值应取（fund_info）中 基金内部代码字段 为 该响应结果的 分级产品代码 的记录 的基金备案编码fund_code字段）
                    .trustNo(trustNo)
                    .admin(tenant.getTenantName())
                    .createTime(now)
                    .updateTime(now)
                    .build());
        }
        return result;
    }


    private void processInvestorInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo) {
        processInvestorInfoDetailData(tenant, trustNo, "0");
//        try {
//        Thread.sleep(10 * 60 * 1000);
//        } catch (InterruptedException e) {
//            log.error("processCustInfoData: Thread.sleep(); 被打断...", e);
//
//        }
//        processInvestorInfoDetailData(tenant, trustNo, "1");
    }

    private void processInvestorInfoDetailData(TenantEscrowAuthorizationInfoDto tenant, String trustNo, String isShowShareNull) {
        try {
            //3.15 投资者信息查询接口全量
            //1、封装请求参数
            HashMap<String, Object> params = new HashMap<>();
            params.put("isShowShareNull", isShowShareNull);
            //2、获取远端数据
            List<Object> remoteDatas = getRes(GuosenConstants.INVESTOR_INFO_LIST_V2_API, tenant, params, trustNo, null);
            if (!CollectionUtils.isEmpty(remoteDatas)) {
                //3、封装成标准表的对象
                List<InvestorInfoEntity> entities = getInvestorInfoData(tenant, remoteDatas, trustNo);
                //4、调用 basicservice 的保存数据接口
                investorInfoService.saveData(null, entities);
                //
                List<InvestorBankInfoEntity> bankInfoEntities = getInvestorBankInfoData(tenant, remoteDatas, trustNo);
                investorBankInfoService.saveData(null, bankInfoEntities);
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processInvestorInfoDetailData:", e);
            //todo 处理失败
//            pullDataFailureLogService.saveErrorData(tenant.getTenantName(),  PullDataFailureUtil.createErrorMap(trustNo, GuosenConstants.AMAC_CODE_QUERY_LIST_API, null, "appId=" + tenant.getAppKey(), e.getMessage()));
        }
    }

    private List<InvestorBankInfoEntity> getInvestorBankInfoData(TenantEscrowAuthorizationInfoDto tenant, List<Object> remoteDatas, String trustNo) {
        //1、获取字典表数据
        //2、封装数据
        List<InvestorBankInfoEntity> result = new ArrayList<>();
        HashMap<String, String> remoteDataMap;
        Date now = new Date();
        for (Object remoteData : remoteDatas) {
            remoteDataMap = (HashMap<String, String>) remoteData;
            result.add(InvestorBankInfoEntity.builder()
                    .nameInBank(remoteDataMap.get("accountName"))
                    .bankNo(remoteDataMap.get("bankAccount"))
                    .branchBankName(remoteDataMap.get("bankName"))
                    .fundAcco(remoteDataMap.get("fundAccount"))
                    .tradeAcco(remoteDataMap.get("tradeAccount"))
                    .bankNoType("银行账号")
                    .trustNo(trustNo)
                    .admin(tenant.getTenantName())
                    .createTime(now)
                    .updateTime(now)
                    .build());
        }
        return result;



    }

    private List<InvestorInfoEntity> getInvestorInfoData(TenantEscrowAuthorizationInfoDto tenant, List<Object> remoteDatas, String trustNo) {
        //1、获取字典表数据
        Map<String, String> custTypeCsdcDic = dataDictionaryBasicService.queryWithDetail(DataDictionaryTypeEnum.CUST_TYPE_CSDC_SERVICE, trustNo);
        Map<String, Map<String, String>> identityTypeCsdcDic = certificateCsdcService.queryCertificateWithDetail(trustNo);
        //2、封装数据
        List<InvestorInfoEntity> result = new ArrayList<>();
        HashMap<String, String> remoteDataMap;
        Date now = new Date();
        for (Object remoteData : remoteDatas) {
            remoteDataMap = (HashMap<String, String>) remoteData;
            String key = MatchDataDictionaryKeyUtil.getKey(custTypeCsdcDic, remoteDataMap.get("customerType"));
            result.add(InvestorInfoEntity.builder()
                    .custName(remoteDataMap.get("customerName"))
                    .custType(key)
                    .fundAcco(remoteDataMap.get("fundAccount"))
                    .identityNo(remoteDataMap.get("identityNo"))
                    .identityType(MatchDataDictionaryKeyUtil.getKey(key, identityTypeCsdcDic, remoteDataMap.get("identityType")))
                    .trustNo(trustNo)
                    .admin(tenant.getTenantName())
                    .createTime(now)
                    .updateTime(now)
                    .build());
        }
        return result;
    }

    private static final ThreadLocal<Map<String, String>> fundInnerCodeAndCodeTL = new ThreadLocal<>();

    public void processFundInfoData(TenantEscrowAuthorizationInfoDto tenant, String trustNo) {
        try {
            //1、封装请求参数
            HashMap<String, Object> params = new HashMap<>();
            //2、获取远端数据
            List<Object> remoteDatas = getRes(GuosenConstants.AMAC_CODE_QUERY_LIST_API, tenant, params, trustNo, null);
            if (!CollectionUtils.isEmpty(remoteDatas)) {
                //3、封装成标准表的对象
                List<FundInfoEntity> entities = getFundInfoData(tenant, remoteDatas, trustNo);
                //4、调用 basicservice 的保存数据接口
                fundInfoService.saveData(null, entities);
                //2.3 将基金备案代码与内部代码放入tl中
                Map<String, String> innerFundCodeMap = new HashMap<>();
                entities.forEach(fundInfoEntity -> {
                    innerFundCodeMap.put(fundInfoEntity.getInnerFundCode(), fundInfoEntity.getFundCode());
                });
                fundInnerCodeAndCodeTL.set(innerFundCodeMap);
            }
        } catch (Exception e) {
            log.error("OutsourceGuosenServiceImpl:processFundInfoData:", e);
            //todo 处理失败
//            pullDataFailureLogService.saveErrorData(tenant.getTenantName(),  PullDataFailureUtil.createErrorMap(trustNo, GuosenConstants.AMAC_CODE_QUERY_LIST_API, null, "appId=" + tenant.getAppKey(), e.getMessage()));
        }

    }

    private List<FundInfoEntity> getFundInfoData(TenantEscrowAuthorizationInfoDto tenant, List<Object> remoteDatas, String trustNo) {
        //1、获取字典表数据 //此接口不需要
        //2、封装数据
        List<FundInfoEntity> result = new ArrayList<>();
        HashMap<String, String> remoteDataMap;
        Date now = new Date();
        for (Object remoteData : remoteDatas) {
            remoteDataMap = (HashMap<String, String>) remoteData;
            result.add(FundInfoEntity.builder()
                    .innerFundCode(remoteDataMap.get("structuredFundCode"))
                    .fundName(remoteDataMap.get("structuredFundName"))
                    .fundCode(remoteDataMap.get("amacCode"))
                    .fundLevel(remoteDataMap.get("structuredFundCode").equals(remoteDataMap.get("fundCode")) ? null : "2")
                    .trustNo(trustNo)
                    .admin(tenant.getTenantName())
                    .createTime(now)
                    .updateTime(now)
                    .build());
        }
        return result;
    }

    private List<Object> getRes(String url, TenantEscrowAuthorizationInfoDto tenant, HashMap<String, Object> params, String trustNo, ReentrantLock lock, int waitTime, int sleepTime) {
        List<Object> remoteResp = null;
        try {
            lock.tryLock(waitTime, TimeUnit.SECONDS);
            remoteResp = getRes(url, tenant, params, trustNo, null);
        } catch (Exception e) {
            log.error("url:" + url + ":error:", e);
            Thread.currentThread().interrupt();
        } finally {
            try {
                Thread.sleep(sleepTime + 1000L);
            } catch (Exception e) {
                log.error("睡眠的线程被打断了",e);
                Thread.currentThread().interrupt();
            }
            //todo baocuo
            lock.unlock();
        }
        return remoteResp;
    }

    @Autowired
    private InvokeRecordService invokeRecordService;

    private ObjectMapper objectMapper = new ObjectMapper();

    private List<Object> getRes(String url, TenantEscrowAuthorizationInfoDto tenant, HashMap<String, Object> params, String trustNo, InvokeGuosenRecordDto dto) {
        HttpHeaders headers = null;
        HttpEntity<JSONObject> res = null;
        String errorMsg = null;
        String tenantAppSecret = tenant.getAppSecret();
        if (ObjectUtil.isEmpty(tenantAppSecret)){
            return new ArrayList<>();
        }
        GuosenAppSecretDTO guosenAppSecretDTO = new GuosenAppSecretDTO();
        try {
            guosenAppSecretDTO = objectMapper.readValue(tenantAppSecret, GuosenAppSecretDTO.class);
        } catch (IOException e) {
            log.error("解析托管账密{}失败:{}", JSONUtil.toJsonStr(guosenAppSecretDTO), e);
        }
        String appId = guosenAppSecretDTO.getAppId();
        String appSecret = guosenAppSecretDTO.getAppSecret();
        String managerCode = guosenAppSecretDTO.getManagerCode();
        try {
            params.put("managerCode", managerCode);
            params.put("appId", appId);
            StringBuffer stringBuffer = new StringBuffer();
            params.keySet().stream().sorted().collect(Collectors.toCollection(LinkedHashSet::new)).forEach(key -> {
                stringBuffer.append(key).append("=").append(params.get(key)).append("&");
            });
            String paramStr = stringBuffer.toString();
            String paramStrForSign = appSecret + paramStr.replace("&", "").replace("=", "");
            log.info("加密前： " + JSON.toJSONString(paramStrForSign));
            //2、 签名
            String sign = MD5Util.getMD5Str(paramStrForSign);
            log.info("加密后： " + JSON.toJSONString(sign));
            //3、将签名加到参数中
            paramStr += "sign=" + sign;
            headers = new HttpHeaders();
            String urlWithParam = guosenUrlPrefix + url + "?" + paramStr;
            log.info("urlWithParam = " + urlWithParam);
            res = restTemplate
                    .exchange(urlWithParam,
                            HttpMethod.GET,
                            new HttpEntity<>(null, headers),
                            JSONObject.class);
            //todo 保存结果
            log.info("the url is {} ; the params is {} ; the res is {}", url, JSON.toJSONString(params), JSON.toJSONString(res));
            log.info("调用结果: " + JSON.toJSONString(res));

            if (!Objects.isNull(res)) {
                JSONObject body = res.getBody();
                if (!ObjectUtils.isEmpty(body)) {
                    RemoteDto remoteDto = body.toJavaObject(RemoteDto.class);
                    List<Object> returnObject = remoteDto.getReturnObject();
                    if ("200".equals(remoteDto.getCode())) {
                        Date now = new Date();
                        InvokeRecordEntity invokeRecordEntity = InvokeRecordEntity.builder()
                                .trustNo(trustNo)
                                .admin(tenant.getTenantName())
                                .invokeType(url)
                                .reqHeader(JSON.toJSONString(headers))
                                .reqParam(paramStr)
                                .invokeTime(now)
                                .invokeResult(JSON.toJSONString(body))
                                .queryKey(tenant.getTenantName() + "::" + trustNo + "::" + url)
                                .createTime(now)
                                .updateTime(now)
                                .build();
                        invokeRecordService.save(invokeRecordEntity);
                        return returnObject;
                    } else if ("该接口请求间隔时间太短，请稍后再试".equals(remoteDto.getMessage()) && !Objects.isNull(dto)) {
                        CacheAdminTrustNoTypeMap.put(dto.getKey(), CustodyContants.UPDATE_FLAG, dto);
                    }
                }
            }
        } catch (Exception e) {
            log.error("调用国信托管失败: the url is {} ; the params is {} ", url, JSON.toJSONString(params));
            log.error("调用国信托管失败：", e);
            errorMsg = e.getMessage();
        }
        if (StringUtils.isBlank(errorMsg) && !ObjectUtils.isEmpty(res)){
            errorMsg = JSON.toJSONString(res.getBody());
        }
        //将错误的信息塞入错误表
        pullDataFailureLogService.saveErrorData(tenant.getTenantName(), PullDataFailureUtil.createErrorMap(trustNo, url, headers, "appId=" + appId, errorMsg));
        return null;
    }

    @Override
    public R updateDataDaily(List<TenantEscrowAuthorizationInfoDto> tenants) {
        return null;
    }

    @Override
    public R updateSingleDataDaily(TenantEscrowAuthorizationInfoDto tenant) {
        log.info("国信托管数据更新...");
        getDataFromGuosenAt7Am(tenant, CustodyContants.UPDATE_BY_MONTH_DAILY_LOOP_TIME);
        log.info("国信托管数据更新结束:");
        return R.ok("国信托管数据更新结束！");
    }

    /**
     * 每间隔12分钟执行一次，（接口间隔限制10分钟）
     * @return
     */
    @Override
    public R PullRemoteDataFirstQueueJob() {
        //todo 所有的方法都在这
        log.info("PullRemoteDataFirstQueueJob 执行了...");
//        executeQuery("22", GuosenConstants.NET_VALUE_INFO_LIST_API);
        executeQuery("22", GuosenConstants.TURN_OVER_LIST_V2_API);
        executeQuery("22", GuosenConstants.ACCOUNT_HISTORY_DETAIL_LIST_V2_API);
        executeQuery("22", GuosenConstants.TA_CONFIRM_INFO_LIST_API);
        executeQuery("22", GuosenConstants.VALUATION_LIST_V2_API);
        executeQuery("22", GuosenConstants.PERSONAL_REWARD_LIST_API);
        executeQuery("22", GuosenConstants.CUSTOMER_SHARE_CURRENT_DETAIL_LIST_API);
        executeQuery("22",GuosenConstants.EXPENSE_QUERYLIST);
        executeQuery("22",GuosenConstants.FUND_OPEN_INFO);
        log.info("PullRemoteDataFirstQueueJob 执行结束！");
        return R.ok("ok");
    }
    @Override
    public R initLaterTenantData(String versionStr,List<TenantEscrowAuthorizationInfoDto> tenants) {
        return null;
    }
    private void executeQuery(String trustNo, String type) {
        List<Object> objects = CacheAdminTrustNoTypeMap.get(trustNo, type);
        for (Object object : objects) {
            InvokeGuosenRecordDto dto = (InvokeGuosenRecordDto) object;
            executeQuery(dto);
        }
    }

    public OutsourceGuosenServiceImpl() {
        super();
    }

    @Override
    public R initLaterSingleTenantData(String versionStr, TenantEscrowAuthorizationInfoDto tenant) {
        log.info("guosen:initLaterSingleTenantData is begin...the versionStr is {} ,the tenant is {}", versionStr, JSON.toJSONString(tenant));
        String trustNo = tenant.getTrustNo();
        Integer loopNum = CustodyContants.INIT_BY_MONTH_LOOP_TIME;
//        Integer loopNum = CustodyContants.UPDATE_BY_MONTH_DAILY_LOOP_TIME;
        try {
            switch (versionStr) {
                case CustodyContants.R_PT_S_CUSTODY_3_2_6:
                    //获取托管户信息 accountNumTL
                    getTgAccountNums(trustNo, tenant.getTenantName());
                    //获取 基金内部代码、基金备案编码  fundInnerCodeAndCodeTL
                    getFundInnerCodeAndCode(trustNo);
                    //19 募集⼾流⽔表（MJAccount_seq_info）
                    processMJAccountSeqInfoData(tenant, trustNo, loopNum);
                    //17 托管⼾流⽔表（TGAccount_seq_info）(托管户历史流水查询)
                    processTGAccountSeqInfoHistoryData(tenant, trustNo, loopNum);
                    break;
            }
        }catch(Exception e){
            log.error("国信流水表更新失败!", e);
        }
        return null;
    }

    private void initTenantDataBy304(TenantEscrowAuthorizationInfoDto tenant, String trustNo, Integer loopNum) {
        try {
            //21基金开放日(fund_open_info)
            processFundOpenInfoData(tenant,trustNo,loopNum);
            log.info("318patch01 : 国信更新fundOpenInfo结束！");
        } catch (Exception e) {
            log.error("guosen:initTenantDataBy304 is error:", e);
        } finally {
            fundInnerCodeAndCodeTL.remove();
        }
    }

    @Override
    public  R initByAppointedTrustNoAndMouth(TenantEscrowAuthorizationInfoDto tenant, int mouth) {
        log.info("GUOSEN:initByAppointedTrustNoAndMouth_tenant={},mouth={}",JSON.toJSONString(tenant),mouth);
        getDataFromGuosen(tenant,mouth);
        return R.ok();
    }

    @Override
    public R updateData20min(List<TenantEscrowAuthorizationInfoDto> data) {
        return null;
    }

    @Override
    public R updateSingleData20min(TenantEscrowAuthorizationInfoDto tenant) {
        try {
            log.info("国泰托管数据每30min更新新...");
            String trustNo = tenant.getTrustNo();
            Integer loopTime = CustodyContants.UPDATE_BY_MONTH_DAILY_LOOP_TIME;
            //获取托管户信息 accountNumTL
            getTgAccountNums(trustNo,tenant.getTenantName());
            //获取 基金内部代码、基金备案编码  fundInnerCodeAndCodeTL
            getFundInnerCodeAndCode(trustNo);

            //19 募集⼾流⽔表（MJAccount_seq_info）
            processMJAccountSeqInfoData(tenant, trustNo, loopTime);
            //16 募集⼾银⾏账⼾余额信息表（MJAccount_balance_info）
            processMJAccountBalanceInfoData(tenant, trustNo);
            //17 托管⼾流⽔表（TGAccount_seq_info）(2.1 托管户实时流水查询接口)
            processTGAccountSeqInfoCurrentData(tenant, trustNo, loopTime);
            //16 托管⼾银⾏账⼾余额信息表（TGAccount_balance_info）
            processTGAccountBalanceInfoData(tenant, trustNo);
            log.info("国泰托管数据每30min更新新结束:");
            return R.ok("国泰托管数据每30min跟新完成");
        }finally{
            accountNumTL.remove();
            fundInnerCodeAndCodeTL.remove();
        }
    }

    @Override
    public R updateData1h(List<TenantEscrowAuthorizationInfoDto> data) {
        return null;
    }

    @Override
    public R updateSingleData1h(TenantEscrowAuthorizationInfoDto tenant) {
        return null;
    }

    @Override
    public R updateDataCJ(String date, String flag, TenantEscrowAuthorizationInfoDto tenant) {
        return null;
    }

    @Override
    public R updateNetValueData(List<TenantEscrowAuthorizationInfoDto> tenants) {
        return null;
    }

    @Override
    public R updateSingleNetValueData(TenantEscrowAuthorizationInfoDto tenant) {
        try {
            log.info("国泰托管数据每8点、12点、20点更新开始...");
            String trustNo = tenant.getTrustNo();
            Integer loopTime = CustodyContants.UPDATE_BY_MONTH_DAILY_LOOP_TIME;
            processFundValueTableDataInfoData(tenant, trustNo, loopTime);
            log.info("国泰托管数据每8点、12点、20点更新结束:");
        }catch (Exception e){
            log.error("国泰托管数据每8点、12点、20点更新异常！");
        }
        return R.ok("国泰托管数据每8点、12点、20点更新完成");
    }

    @Override
    public DubboResult updateMJBalanceData(TenantEscrowAuthorizationInfoDto tenant) {
        try {
            // 募集户银行账户余额信息表（MJAccount_balance_info）
            processMJAccountBalanceInfoData(tenant, tenant.getTrustNo());
            return DubboResult.newSuccess();
        } catch (Exception e) {
            log.error("手动更新募集户余额失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "手动更新募集户余额失败" + e.getMessage());
        }
    }

    @Override
    public DubboResult updateMJSeqData(TenantEscrowAuthorizationInfoDto tenant) {
        try {
            // 募集户流水表（MJAccount_seq_info）
            processMJAccountSeqInfoData(tenant, tenant.getTrustNo(), 1);
            return DubboResult.newSuccess();
        } catch (Exception e) {
            log.error("手动更新募集户流水失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "手动更新募集户流水失败" + e.getMessage());
        }
    }

    @Override
    public DubboResult updateTGBalanceData(TenantEscrowAuthorizationInfoDto tenant) {
        try {
            // 托管户银行账户余额信息表（TGAccount_balance_info）
            processTGAccountBalanceInfoData(tenant, tenant.getTrustNo());
            return DubboResult.newSuccess();
        } catch (Exception e) {
            log.error("手动更新托管户余额失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "手动更新托管户余额失败" + e.getMessage());
        }
    }

    @Override
    public DubboResult updateTGSeqData(TenantEscrowAuthorizationInfoDto tenant) {
        try {
            // 托管户流水表（TGAccount_seq_info）(2.1 托管户实时流水查询接口)
            processTGAccountSeqInfoCurrentData(tenant, tenant.getTrustNo(), 1);
            return DubboResult.newSuccess();
        } catch (Exception e) {
            log.error("手动更新托管户流水失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "手动更新托管户流水失败" + e.getMessage());
        }
    }

    public void getTgAccountNums(String trustNo, String admin){
        List<AccountInfoEntity> tgAccountInfoList = accountInfoService.getDataList(trustNo, HtscConstants.TG_ACCOUNT_TYPE_CODE,admin);
        List<String> accountNums = new ArrayList<>();
        for (AccountInfoEntity tgAccountInfo : tgAccountInfoList){
            accountNums.add(tgAccountInfo.getBankAcco());
        }
        accountNumTL.set(accountNums);
    }
    public void getFundInnerCodeAndCode(String trustNo){
        Map<String, String> innerFundCodeMap = new HashMap<>();
        List<FundInfoEntity> fundInfoList = fundInfoService.getDataByTrustNo(trustNo);
        for (FundInfoEntity fundInfo : fundInfoList){
            innerFundCodeMap.put(fundInfo.getInnerFundCode(),fundInfo.getFundCode());
        }
        fundInnerCodeAndCodeTL.set(innerFundCodeMap);
    }


}
