package com.cusc.nirvana.user.rnr.fp.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cache.CacheFactory;
import com.cache.exception.CacheException;
import com.cusc.nirvana.common.result.Response;
import com.cusc.nirvana.user.ciam.client.CiamUserClient;
import com.cusc.nirvana.user.ciam.dto.CiamUserDTO;
import com.cusc.nirvana.user.exception.CuscUserException;
import com.cusc.nirvana.user.rnr.fp.common.CmccCertTypeEnum;
import com.cusc.nirvana.user.rnr.fp.common.MbCodeEnum;
import com.cusc.nirvana.user.rnr.fp.common.ResponseCode;
import com.cusc.nirvana.user.rnr.fp.common.RnrResultEnum;
import com.cusc.nirvana.user.rnr.fp.common.cloud.CloudMethod;
import com.cusc.nirvana.user.rnr.fp.common.cloud.CloudService;
import com.cusc.nirvana.user.rnr.fp.common.config.EncryptionConfig;
import com.cusc.nirvana.user.rnr.fp.common.config.RnrFpConfig;
import com.cusc.nirvana.user.rnr.fp.constants.AuthWayTypeEnum;
import com.cusc.nirvana.user.rnr.fp.constants.LivenessTypeEnum;
import com.cusc.nirvana.user.rnr.fp.constants.RedisConstant;
import com.cusc.nirvana.user.rnr.fp.constants.ResultCodeEnum;
import com.cusc.nirvana.user.rnr.fp.dto.AttachmentDTO;
import com.cusc.nirvana.user.rnr.fp.dto.AttachmentRespDTO;
import com.cusc.nirvana.user.rnr.fp.dto.CardNumReqDTO;
import com.cusc.nirvana.user.rnr.fp.dto.CardNumberCmccCheckDTO;
import com.cusc.nirvana.user.rnr.fp.dto.CertCheckReqDTO;
import com.cusc.nirvana.user.rnr.fp.dto.CustomerInfoDTO;
import com.cusc.nirvana.user.rnr.fp.dto.EncryptionKeyDTO;
import com.cusc.nirvana.user.rnr.fp.dto.FaceCompareReqDTO;
import com.cusc.nirvana.user.rnr.fp.dto.FaceCompareRespDTO;
import com.cusc.nirvana.user.rnr.fp.dto.H5ValidationOCRReqDTO;
import com.cusc.nirvana.user.rnr.fp.dto.IdCompareReqDTO;
import com.cusc.nirvana.user.rnr.fp.dto.IdCompareRespDTO;
import com.cusc.nirvana.user.rnr.fp.dto.IovCardNumRespDTO;
import com.cusc.nirvana.user.rnr.fp.dto.LiveLoginReqDTO;
import com.cusc.nirvana.user.rnr.fp.dto.LiveLoginRespDTO;
import com.cusc.nirvana.user.rnr.fp.dto.LivenessCodeReqDTO;
import com.cusc.nirvana.user.rnr.fp.dto.LivenessCodeRequestDTO;
import com.cusc.nirvana.user.rnr.fp.dto.LivenessCodeResponseDTO;
import com.cusc.nirvana.user.rnr.fp.dto.LivenessReqDTO;
import com.cusc.nirvana.user.rnr.fp.dto.LivenessRespDTO;
import com.cusc.nirvana.user.rnr.fp.dto.LivenessResultDTO;
import com.cusc.nirvana.user.rnr.fp.dto.OcrNpReqDTO;
import com.cusc.nirvana.user.rnr.fp.dto.OcrNpRespDTO;
import com.cusc.nirvana.user.rnr.fp.dto.PersonH5CallBackRequestDTO;
import com.cusc.nirvana.user.rnr.fp.dto.RnrRequestDTO;
import com.cusc.nirvana.user.rnr.fp.dto.RnrResponseDTO;
import com.cusc.nirvana.user.rnr.fp.dto.RnrReturnDTO;
import com.cusc.nirvana.user.rnr.fp.dto.T1CommonResponseDTO;
import com.cusc.nirvana.user.rnr.fp.dto.UploadResultDto;
import com.cusc.nirvana.user.rnr.fp.dto.VinCardDTO;
import com.cusc.nirvana.user.rnr.fp.service.*;
import com.cusc.nirvana.user.rnr.fp.util.SnowflakeIdWorker;
import com.cusc.nirvana.user.rnr.mg.client.ChangeOrUnbindIccidsClient;
import com.cusc.nirvana.user.rnr.mg.client.MgRnrAuthenticationResultClient;
import com.cusc.nirvana.user.rnr.mg.client.MgRnrCardInfoClient;
import com.cusc.nirvana.user.rnr.mg.client.MgRnrFileClient;
import com.cusc.nirvana.user.rnr.mg.client.MgRnrInfoClient;
import com.cusc.nirvana.user.rnr.mg.client.MgRnrLiaisonInfoClient;
import com.cusc.nirvana.user.rnr.mg.client.MgRnrRelationClient;
import com.cusc.nirvana.user.rnr.mg.client.OrgSimRelClient;
import com.cusc.nirvana.user.rnr.mg.client.RnrOrderClient;
import com.cusc.nirvana.user.rnr.mg.constants.*;
import com.cusc.nirvana.user.rnr.mg.dto.MgRnrAuthenticationResultDTO;
import com.cusc.nirvana.user.rnr.mg.dto.MgRnrCardInfoDTO;
import com.cusc.nirvana.user.rnr.mg.dto.MgRnrFileDTO;
import com.cusc.nirvana.user.rnr.mg.dto.MgRnrInfoDTO;
import com.cusc.nirvana.user.rnr.mg.dto.MgRnrLiaisonInfoDTO;
import com.cusc.nirvana.user.rnr.mg.dto.RnrOrderDTO;
import com.cusc.nirvana.user.rnr.mg.dto.RnrRelationDTO;
import com.cusc.nirvana.user.util.CuscStringUtils;
import com.cusc.nirvana.user.util.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author stayAnd
 * @date 2022/4/18
 */
@Service
@Slf4j
public class RnrServiceImpl implements IRnrService {

    @Resource
    private CloudService cloudService;

    @Resource
    private CacheFactory cacheFactory;

    @Resource
    private ExecutorService rnrExecutorService;

    @Resource
    private MgRnrCardInfoClient mgRnrCardInfoClient;

    @Resource
    private MgRnrInfoClient rnrInfoClient;

    @Resource
    private RnrOrderClient orderClient;

    @Resource
    private IFileService fileService;

    @Resource
    private MgRnrRelationClient mgRnrRelationClient;

    @Resource
    private MgRnrFileClient rnrFileClient;

    @Resource
    private CiamUserClient ciamUserClient;

    @Resource
    private MgRnrAuthenticationResultClient authenticationResultClient;

    @Autowired
    private RnrFpConfig rnrFpConfig;

    @Resource
    private MgRnrCardInfoClient rnrCardInfoClient;

    @Resource
	private MgRnrLiaisonInfoClient liaisonInfoClient;

	@Resource
	private ChangeOrUnbindIccidsClient changeOrUnbindIccidsClient;

    @Value("${T1.power:true}")
    private Boolean power;

    @Value("${T1.cmccUrl:}")
    private String cmccUrl;

    @Value("${T1.ctccUrl:}")
    private String ctccUrl;

    @Autowired
    private OrgSimRelClient orgSimRelClient;

    @Resource
    private OperatorService operatorService;

    @Autowired
    private EncryptionConfig encryptionConfig;

    @Resource
    private IT1UploadService it1UploadService;

    @Autowired
    @Qualifier("noBalanceRestTemplateRnrFpT1")
    RestTemplate noBalanceRestTemplateRnrFpT1;

    @Resource
	private INewVinCardService newVinCardService;

    /**
     * 进行云端人脸和人证对比
     *
     * @param dto
     * @param livenessPic
     * @return
     */
    @Override
    public RnrResponseDTO doIdAndFaceCompare(RnrRequestDTO dto, String[] livenessPic, Integer subjectType) {
        RnrResponseDTO rnrResponseDTO = new RnrResponseDTO();
        String[] bestFrameList = null;
        if (null == livenessPic) {
            // 未做过活体
            // 活体检测检测 如果失败的话 不需要进行下面 ocr 人证比对 人脸比对
            bestFrameList = doLiveness(dto, rnrResponseDTO);
            if (!Boolean.TRUE.equals(rnrResponseDTO.getLivenessResult())) {
                // 活体失败直接返回
                throw new CuscUserException("", rnrResponseDTO.getLivenessFailMsg());
            }
        } else {
            // 已经做过活体信息
            bestFrameList = livenessPic;
            rnrResponseDTO.setLivenessResult(true);
        }
        String bestFrame = bestFrameList[0];
        // 人证比对
        Future<Response<IdCompareRespDTO>> idCompareFuture =
                doIdCompare(dto, bestFrame, dto.getMatchVideoPerosinInfo().getCertNumber(),
                        dto.getMatchVideoPerosinInfo().getFullName());

        // 人脸比对
        Future<Response<FaceCompareRespDTO>> faceCompareFuture =
                doFaceCompare(bestFrame, dto.getMatchVideoPerosinInfo().getFacePicBase64(), dto);

        try {
            Response<IdCompareRespDTO> response = idCompareFuture.get(5, TimeUnit.SECONDS);
            if (!response.isSuccess()) {
                recordRnrResultLog(dto, AuthWayTypeEnum.ID_COMPARE, "2", "人证比对失败,:" + response.getMsg(), subjectType);
                rnrResponseDTO.setIdCompareFailMsg("人证比对失败,:" + response.getMsg());
            } else {
                String similarity = response.getData().getSimilarity();
                if (Integer.parseInt(similarity) > Integer.parseInt(rnrFpConfig.getIdCompareSimilarity())) {
                    rnrResponseDTO.setIdCompareResult(true);
                    recordRnrResultLog(dto, AuthWayTypeEnum.ID_COMPARE, "1", "人证比对成功,相似度:" + similarity, subjectType);
                } else {
                    recordRnrResultLog(dto, AuthWayTypeEnum.ID_COMPARE, "2", "人证比对失败,相似度:" + similarity, subjectType);
                    rnrResponseDTO.setIdCompareFailMsg("人证比对失败,相似度:" + similarity);
                }
            }
        } catch (Exception e) {
            log.error("videoPersonInfoProcess idCompare Future has error,fullName = {},certNumber = {}",
                    dto.getMatchVideoPerosinInfo().getFullName(), dto.getMatchVideoPerosinInfo().getCertNumber(), e);
            recordRnrResultLog(dto, AuthWayTypeEnum.ID_COMPARE, "2", "人证比对异常:请求异常", subjectType);
            rnrResponseDTO.setIdCompareFailMsg("人证比对异常:请求异常");
        }

        try {
            Response<FaceCompareRespDTO> response = faceCompareFuture.get(5, TimeUnit.SECONDS);
            if (!response.isSuccess()) {
                recordRnrResultLog(dto, AuthWayTypeEnum.FACE_COMPARE, "2", "人脸比对失败:" + response.getMsg(), subjectType);
                rnrResponseDTO.setFaceCompareFailMsg("人脸比对失败:" + response.getMsg());
            } else {
                String similarity = response.getData().getSimilarity();
                if (Integer.parseInt(similarity) > Integer.parseInt(rnrFpConfig.getFaceCompareSimilarity())) {
                    rnrResponseDTO.setFaceCompareResult(true);
                    recordRnrResultLog(dto, AuthWayTypeEnum.FACE_COMPARE, "1", "人脸比对成功,相似度:" + similarity, subjectType);
                } else {
                    recordRnrResultLog(dto, AuthWayTypeEnum.FACE_COMPARE, "2", "人脸比对失败,相似度:" + similarity, subjectType);
                    rnrResponseDTO.setFaceCompareFailMsg("人脸比对失败,相似度:" + similarity);
                }
            }
        } catch (Exception e) {
            log.error("doRnr ,videoPersonInfoProcess faceCompare Future has error,fullName = {},certNumber = {}",
                    dto.getMatchVideoPerosinInfo().getFullName(), dto.getMatchVideoPerosinInfo().getCertNumber(), e);
            recordRnrResultLog(dto, AuthWayTypeEnum.FACE_COMPARE, "2", "人脸比对异常:请求异常", subjectType);
            rnrResponseDTO.setFaceCompareFailMsg("人脸比对异常:请求异常");
        }
        return rnrResponseDTO;
    }

    /**
     * 请求云端获取活体验证码
     *
     * @param dto 请求参数
     * @return
     */
    @Override
    public LivenessCodeResponseDTO getLivenessCode(LivenessCodeRequestDTO dto) {
        log.info("云端获取活体验证码,{}", dto);
        //生成唯一的id
        String requestId = UUID.randomUUID().toString();
        String livenessCode = null;
        LivenessCodeReqDTO codeDto = new LivenessCodeReqDTO();
        codeDto.setSerialNumber(dto.getSerialNumber());
        codeDto.setBizUuid(dto.getBizUuid());

        if (LivenessTypeEnum.LIP.getCode().equalsIgnoreCase(rnrFpConfig.getLivenessType())) {
            //唇语验证，数字
            Response<LivenessCodeResponseDTO> livenessCodeResponse =
                    cloudService.postForResponse(CloudMethod.LIVENESS_CODE_TIP, codeDto, LivenessCodeResponseDTO.class);
            if (null == livenessCodeResponse || !livenessCodeResponse.isSuccess()
                    || livenessCodeResponse.getData() == null) {
                throw new CuscUserException("", "获取活体数字验证码失败");
            }

            livenessCode = livenessCodeResponse.getData().getLivenessCode();
        } else if (LivenessTypeEnum.ACTION.getCode().equalsIgnoreCase(rnrFpConfig.getLivenessType())) {
            //动作验证，左右摇头
            Response<LivenessCodeResponseDTO> resp =
                    cloudService.postForResponse(CloudMethod.LIVENESS_CODE_ACTION, codeDto,
                            LivenessCodeResponseDTO.class);
            if (null == resp || !resp.isSuccess() || resp.getData() == null) {
                throw new CuscUserException("", "获取活体动作序列失败");
            }

            livenessCode = resp.getData().getActionSeq();
        }

        // redis存储requestId 和 livenessCode 的关系
        try {
            cacheFactory.ExpireString()
                    .setExpireValue(RedisConstant.USER_RNR_LIVENESSCODE_PREFIX + requestId, livenessCode, 7200);
        } catch (Exception e) {
            log.error("getLivenessCode has error , dto = {}", JSONObject.toJSONString(dto), e);
        }

        LivenessCodeResponseDTO result =
                new LivenessCodeResponseDTO().setLivenessCode(livenessCode).setRequestId(requestId);
        result.setLivenessType(rnrFpConfig.getLivenessType());
        return result;
    }

    @Override
    public Integer personRnr(String serialNumber, String requestId, RnrRelationDTO dto) {
        log.info("personRnr serialNumber = {},requestId = {},dto = {}", serialNumber, requestId,
                JSONObject.toJSONString(dto));
        boolean sendWorkOrder = checkPersonData(serialNumber, requestId, dto);
        Response response = mgRnrRelationClient.saveRnrRelation(dto);
        if (!response.isSuccess()) {
            throw new CuscUserException(response.getCode(), response.getMsg());
        }
        // 如果不需要发送工单,则立即发送云端系统
        if (!sendWorkOrder && response.isSuccess()) {
            this.notifyResult(dto.getCardList(), RnrBizzTypeEnum.Bind.getCode().toString(), serialNumber, null,
                    ResultCodeEnum.PASS.getCode());
        }
        return sendWorkOrder ? RnrResultEnum.REVIEW.getCode() : RnrResultEnum.PASS.getCode();
    }

    /**
     * 校验个人信息
     *
     * @param serialNumber
     * @param requestId
     * @param dto
     * @return 是否需要发送工单
     */
    @Override
    public boolean checkPersonData(String serialNumber, String requestId, RnrRelationDTO dto) {
        // 卡实名状态校验
        if (dto.getIsSecondHandCar() == 0) {
            iccidListCheck(dto);
        }

        // 一证十卡校验
        cardNumberCheck(dto);

        RnrRequestDTO rnrRequest = rnrPersonRelationDto2RnrRequestDTO(dto);
        rnrRequest.setBizUuid(dto.getInfo().getUuid());
        rnrRequest.setSerialNumber(dto.getOrder().getUuid());
        rnrRequest.setRequestId(requestId);

        RnrResponseDTO rnrResponseDTO = doPersonRnr(rnrRequest, null);

        // 添加活体图片1
        MgRnrFileDTO livenessPic1 = new MgRnrFileDTO();
        livenessPic1.setUuid(CuscStringUtils.generateUuid());
        livenessPic1.setRnrBizzType(RnrBizzTypeEnum.Bind.getCode());
        livenessPic1.setRnrId(dto.getInfo().getUuid());
        livenessPic1.setLiaisonId(rnrRequest.getMatchVideoPerosinInfo().getPersonId());
        livenessPic1.setFileSystemId(rnrResponseDTO.getLivenessPic1());
        livenessPic1.setFileType(RnrFileType.LIVENESS_SCREEN_FIRST.getCode());
        livenessPic1.setTenantNo(dto.getTenantNo());
        livenessPic1.setRoutingKey(dto.getInfo().getRoutingKey());
        livenessPic1.setCreator(dto.getInfo().getCreator());
        livenessPic1.setFileName("活体视频1.png");
        dto.getRnrFileList().add(livenessPic1);

        // 添加活体图片1
        MgRnrFileDTO livenessPic2 = new MgRnrFileDTO();
        livenessPic2.setUuid(CuscStringUtils.generateUuid());
        livenessPic2.setRnrBizzType(RnrBizzTypeEnum.Bind.getCode());
        livenessPic2.setRnrId(dto.getInfo().getUuid());
        livenessPic2.setLiaisonId(rnrRequest.getMatchVideoPerosinInfo().getPersonId());
        livenessPic2.setFileSystemId(rnrResponseDTO.getLivenessPic2());
        livenessPic2.setFileType(RnrFileType.LIVENESS_SCREEN_TWO.getCode());
        livenessPic2.setTenantNo(dto.getTenantNo());
        livenessPic2.setRoutingKey(dto.getInfo().getRoutingKey());
        livenessPic2.setCreator(dto.getInfo().getCreator());
        livenessPic2.setFileName("活体视频2.png");
        dto.getRnrFileList().add(livenessPic2);



        // 实名整体结果
        Boolean rnrResult = rnrResponseDTO.getRnrResult();

        // 新车实名通过 && 非委托人模式 && 新车实名 ====> 自动审核
        boolean success = rnrResult && dto.getIsTrust() == 0 && dto.getIsSecondHandCar() == 0;
        // 二手车实名，委托人模式，新车车主非身份证模式都需要发送工单
        if (!dto.getOrder().getSendWorkOrder()) {
            // 不需要发送工单系统,
            dto.getOrder().setSendWorkOrder(!success);
        }
        // 如果新车实名通过并且是本人办理的新车实名则审核类型为自动1,
        dto.getOrder()
                .setAuditType(success ? RnrOrderAuditTypeEnum.AUTO.getCode() : RnrOrderAuditTypeEnum.MANUAL.getCode());
        // 同时设置工单状态为审核通过3
        dto.getOrder()
                .setOrderStatus(success ? RnrOrderStatusEnum.PASS.getCode() : RnrOrderStatusEnum.ASSIGNMENT.getCode());
        // 同时设置实名状态为已实名1
        dto.getInfo().setRnrStatus(success ? RnrStatus.RNR.getCode() : RnrStatus.INIT.getCode());
        dto.getCardList()
                .forEach(card -> card.setRnrStatus(success ? RnrStatus.RNR.getCode() : RnrStatus.INIT.getCode()));
        return dto.getOrder().getSendWorkOrder();
    }

    /**
     * 二手车实名
     *
     * @param serialNumber serialNumber
     * @param requestId    requestId
     * @param dto          dto
     * @return
     */
    @Override
    public Integer personSecondHandRnr(String serialNumber, String requestId, RnrRelationDTO dto) {
        //解绑操作
//		Response response = unboundCards(dto);
//		if(!response.isSuccess()) {
//			throw new CuscUserException("","解绑卡失败，"+response.getMsg());
//		}

        personRnr(serialNumber, requestId, dto);
        return RnrResultEnum.REVIEW.getCode();
    }

    /**
     * 下载&检查 文件
     *
     * @param dto
     * @return key-->人的id  value.key-->文件类型 value.value-->文件base64
     */
    private Map<String, Map<Integer, String>> downLoadFileBase64FromRnrRelationDTO(RnrRelationDTO dto) {
        if (CollectionUtils.isEmpty(dto.getRnrFileList())) {
            return Collections.emptyMap();
        }

        //按照人的信息分组
        Map<String, List<MgRnrFileDTO>> personFileMap =
                dto.getRnrFileList().stream().collect(Collectors.groupingBy(MgRnrFileDTO::getLiaisonId));

        Map<String, Map<Integer, String>> fileMap = new HashMap<>(personFileMap.size());

        personFileMap.forEach((key, fileDTOS) -> {

            //只有身份证和视频需要下载
            List<MgRnrFileDTO> downLoadFileList = fileDTOS.stream()
                    .filter(fileDTO -> RnrFileType.IDENTITY_CARD_BACK.getCode().equals(fileDTO.getFileType())
                            || RnrFileType.IDENTITY_CARD_FRONT.getCode().equals(fileDTO.getFileType())
                            || RnrFileType.LIVENESS_VIDEO.getCode().equals(fileDTO.getFileType()))
                    .collect(Collectors.toList());

            Map<Integer, String> fileInfo = downLoadFileList.parallelStream().collect(
                    Collectors.toMap(MgRnrFileDTO::getFileType,
                            fileDto -> fileService.downLoadBase64(fileDto.getFileSystemId()), (k, v) -> v));

            fileMap.put(key, fileInfo);
        });
        return fileMap;
    }

    @Override
    public void cardNumberCheck(RnrRelationDTO dto) {

        List<MgRnrCardInfoDTO> cardList = dto.getCardList();
        //如果是移动得卡，调用移动一证十卡验证
        //todo 移动一证十卡暂未提供，此功能先屏蔽待后续联调
//        if(power&& com.cusc.nirvana.user.rnr.fp.constants.MbCodeEnum.CMCC.getCode().equals(operatorService.get(cardList.get(0).getIccid()))){
//            cardNumberCmccCheck(dto);
//        }
        // mock模式直接返回
        if (rnrFpConfig.isMock()) {
            return;
        }
        Boolean check = false;
        // 只校验联通的一证十卡
        for (MgRnrCardInfoDTO mgRnrCardInfoDTO : dto.getCardList()) {
            MbCodeEnum mbCodeEnum = MbCodeEnum.get(mgRnrCardInfoDTO.getIccid());
            if (MbCodeEnum.CUCC.getCode().equals(mbCodeEnum.getCode())) {
                check = true;
                log.info("只有联通的卡进行一证十卡验证,{}", mbCodeEnum.getName());
            }
        }
        if (check) {
            CardNumReqDTO query = new CardNumReqDTO();
            query.setCardNo(dto.getInfo().getCertNumber());
            query.setSerialNumber(dto.getInfo().getUuid());
            query.setTenantNo(dto.getTenantNo());
            //todo 调用云端接口
            Response<IovCardNumRespDTO> rnrResponse =
                    cloudService.postForResponse(CloudMethod.IOVCARD_NUM, query, IovCardNumRespDTO.class);
            log.info("认证流程,一证十卡 query = {},response = {}", query.getSerialNumber(),
                    JSONObject.toJSONString(rnrResponse));
            if (null == rnrResponse || !rnrResponse.isSuccess() || null == rnrResponse.getData()) {
                throw new CuscUserException("", "一证十卡查询失败");
            }
            IovCardNumRespDTO data = rnrResponse.getData();
            Integer nowVehicleNum = data.getNowVehicleNum();
            log.info("认证流程,一证十卡云端数量,{}", nowVehicleNum);
            // 还需要查询本地未进行上报的
            MgRnrInfoDTO bean = new MgRnrInfoDTO();
            bean.setCertNumber(dto.getInfo().getCertNumber());
            Response<Integer> numWithUnload = mgRnrCardInfoClient.getNumWithUnload(bean);
            if (numWithUnload.isSuccess()) {
                log.info("认证流程,一证十卡本地未上报数量,{}", numWithUnload.getData());
                nowVehicleNum += numWithUnload.getData();
            }
            log.info("认证流程,一证十卡最终数量,{}", nowVehicleNum);
            if (nowVehicleNum >= rnrFpConfig.getCardNumLimit()) {
                throw new CuscUserException("", "超过一证十卡验证");
            }
        }
    }

    /**
     * @author: jk
     * @description: 移动一证十卡校验
     * @date: 2022/8/4 13:41
     * @version: 1.0
     */
    public void cardNumberCmccCheck(RnrRelationDTO dto) {

        String url = cmccUrl+"/realname/interface/v1/iotIdCheck";

        EncryptionKeyDTO t1VehicleCompanyDTO = encryptionConfig.getKey(dto.getOrder().getOrgId());

        if (t1VehicleCompanyDTO == null) {
            throw new CuscUserException("", "查询车企密钥配置失败，查询参数： 租户编号："+dto.getTenantNo()+"组织id："+dto.getOrder().getOrgId());
        }


        String companyCode = t1VehicleCompanyDTO.getCompanyCode();

        CardNumberCmccCheckDTO cardNumberCmccCheckDTO = new CardNumberCmccCheckDTO();
        cardNumberCmccCheckDTO.setAuth(it1UploadService.getAuth(t1VehicleCompanyDTO));
        cardNumberCmccCheckDTO.setCode(companyCode);

        String requestIdCmss = (null!=t1VehicleCompanyDTO.getPlatFormId()&&t1VehicleCompanyDTO.getPlatFormId().length()>5)?t1VehicleCompanyDTO.getPlatFormId().substring(0,5)+String.valueOf(SnowflakeIdWorker.nextId()):t1VehicleCompanyDTO.getPlatFormId()+String.valueOf(SnowflakeIdWorker.nextId());

        cardNumberCmccCheckDTO.setRequestID(requestIdCmss);

        CustomerInfoDTO customerInfoDTO = new CustomerInfoDTO();
        customerInfoDTO.setCustomerCertNumber(dto.getInfo().getCertNumber());
        customerInfoDTO.setCustomerCertType(CmccCertTypeEnum.getCmccCode(dto.getInfo().getCertType()));
        cardNumberCmccCheckDTO.setCustomerInfo(customerInfoDTO);

        String requestStr = JSON.toJSONString(cardNumberCmccCheckDTO);

        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
        log.info("查询移动一证十卡接口入参{}",requestStr);
        ResponseEntity<String> entity =
                noBalanceRestTemplateRnrFpT1.exchange(url, HttpMethod.POST, new HttpEntity<>(requestStr, requestHeaders),
                        String.class);

        T1CommonResponseDTO result = JSON.parseObject(entity.getBody(), T1CommonResponseDTO.class);

        if(null == result || !"1".equals(result.getOprRst())){
            throw new CuscUserException("", "移动一证十卡查询失败");
        }
        if(!"2".equals(result.getCustomerCheck())){
            throw new CuscUserException("", "超过移动一证十卡验证");
        }
    }
    @Override
    public void iccidListCheck(RnrRelationDTO dto) {
        if (CollectionUtils.isEmpty(dto.getCardList())) {
            throw new CuscUserException("", "卡信息为空");
        }

        List<String> iccidList =
                dto.getCardList().stream().map(MgRnrCardInfoDTO::getIccid).collect(Collectors.toList());
        MgRnrCardInfoDTO query = new MgRnrCardInfoDTO();
        query.setIccidList(iccidList);
        Response<List<MgRnrCardInfoDTO>> response = mgRnrCardInfoClient.queryByList(query);
        if (null == response || !response.getSuccess()) {
            throw new CuscUserException("", "查询卡信息失败");
        }

        List<MgRnrCardInfoDTO> cardInfoDTOList = response.getData();
        if (!CollectionUtils.isEmpty(cardInfoDTOList)) {
			List<MgRnrCardInfoDTO> alreadyRnrIccidList = cardInfoDTOList.stream().filter(cardDto -> cardDto.getRnrStatus() == 1).collect(Collectors.toList());


			//非车企实名 需要过滤掉车企实名的卡 允许覆盖
			if (!RnrOrderType.CARMAKER_NEW_VEHICLE.getCode().equals(dto.getOrder().getOrderType())) {
				alreadyRnrIccidList = newVinCardService.filterManufacturerRnr(alreadyRnrIccidList);
			}

            if (!CollectionUtils.isEmpty(alreadyRnrIccidList)) {
                throw new CuscUserException("", "存在已经实名的卡【" + StringUtils.join(alreadyRnrIccidList, ",") + "】");
            }
        }
    }

    @Override
    public String getRnrLivenessType(String tenantNo) {
        //TODO 后期可以基于tenantNo扩展
        return rnrFpConfig.getLivenessType();
    }

    @Override
    public Response notifyResult(List<MgRnrCardInfoDTO> dtoList, String rnrBizzType, String serialNumber, String orgId,
                                 String resultCode) {
        Response response = null;
        try {
            //将实名结果回传给智网（新增）
            List<VinCardDTO> vinCardList = new ArrayList<>();
            dtoList.forEach(mgRnrCardInfoDTO -> {
                VinCardDTO cardDto = new VinCardDTO();
                cardDto.setIccid(mgRnrCardInfoDTO.getIccid());
                cardDto.setVin(mgRnrCardInfoDTO.getIotId());
                cardDto.setOldIccid(mgRnrCardInfoDTO.getOldCardId());
                cardDto.setResultCode(resultCode);
                vinCardList.add(cardDto);
            });
            UploadResultDto resultDto = new UploadResultDto();
            resultDto.setSerialNumber(serialNumber);
            resultDto.setRnrBizzType(rnrBizzType);
            resultDto.setVinCardList(vinCardList);
            resultDto.setOrgId(orgId);
            log.info("通知云端,实名请求是 = {}", resultDto);
            response = cloudService.postForResponse(CloudMethod.NOTIFY_RESULT, resultDto, String.class);
            log.info("通知云端,返回结果是 = {}", response);
            if (!response.isSuccess()) {
                log.error("通知云端失败,serialNumber = {}", serialNumber);
            } else {
                // todo 保存记录,可以添加重试机制
            }
        } catch (Exception e) {
            log.error("实名结果通知云端出现异常 = {}", e.getMessage());
        }

        return response;
    }

    @Override
    public Response<LiveLoginRespDTO> h5LiveLoginUrl(LiveLoginReqDTO dto) {
        return cloudService.postForResponse(CloudMethod.H5LIVELOGIN_URL, dto, LiveLoginRespDTO.class);
    }


    @Override
    public RnrResponseDTO doPersonRnr(RnrRequestDTO dto, String[] livenessPic) {
        RnrResponseDTO rnrResponseDTO = new RnrResponseDTO();

        String[] bestFrameList = null;
        if (null == livenessPic) {
            // 未做过活体
            // 活体检测检测 如果失败的话 不需要进行下面 ocr 人证比对 人脸比对
            bestFrameList = doLiveness(dto, rnrResponseDTO);
            if (!Boolean.TRUE.equals(rnrResponseDTO.getLivenessResult())) {
                //活体失败直接返回
                throw new CuscUserException("", rnrResponseDTO.getLivenessFailMsg());
            }
        } else {
            //已经做过活体信息
            bestFrameList = livenessPic;
            rnrResponseDTO.setLivenessResult(true);

			Integer subjectType = CollectionUtils.isEmpty(dto.getPeorsonList()) ? SubjectTypeEnum.ONESELF.getCode() :
					SubjectTypeEnum.CONSIGNOR.getCode();
			recordRnrResultLog(dto, AuthWayTypeEnum.LIVENESS, "1", "活体成功", subjectType);
        }

        //活体视频 对应人的信息需要做的处理 (ocr & 认证对比 & 人脸对比)
        videoPersonInfoProcess(dto, rnrResponseDTO, bestFrameList[0]);

        //其他人信息的核查(ocr & 人份信息核查)
        personListInfoProcess(dto, rnrResponseDTO);

        //活体认证成功 && ocr认证成功 && 认证对比成功 && 人脸对边成功  ==> 实名认证成功
        Boolean rnrStatus = Boolean.TRUE.equals(rnrResponseDTO.getLivenessResult()) && (
                null != rnrResponseDTO.getVideoPerosonOcResult() && Boolean.TRUE.equals(
                        rnrResponseDTO.getVideoPerosonOcResult().getOcrResult()))
                && Boolean.TRUE.equals(rnrResponseDTO.getIdCompareResult()) && Boolean.TRUE.equals(
                rnrResponseDTO.getFaceCompareResult());


        if (!CollectionUtils.isEmpty(rnrResponseDTO.getPersonListOcrResult())) {
            //如果有其他人的信息 则需要 ocr成功 && 身份核查成功
            rnrStatus = rnrStatus && rnrResponseDTO.getPersonListOcrResult().stream()
                    .allMatch(ocrResultInfo -> null != ocrResultInfo.getOcrResult() && ocrResultInfo.getOcrResult()
                            && null != ocrResultInfo.getCertCheckResult() && ocrResultInfo.getCertCheckResult());
        }
        rnrResponseDTO.setRnrResult(rnrStatus);
        return rnrResponseDTO;
    }

    private void personListInfoProcess(RnrRequestDTO dto, RnrResponseDTO rnrResponseDTO) {
        if (CollectionUtils.isEmpty(dto.getPeorsonList())) {
            return;
        }

        //如果是代理人模式 这里校验的是本人的信息
        Integer subjectType = SubjectTypeEnum.ONESELF.getCode();

        List<RnrResponseDTO.OcrResultInfo> ocrResultInfoList = new ArrayList<>();

        for (RnrRequestDTO.RnrPersonInfo personInfo : dto.getPeorsonList()) {
            //人脸ocr
            Future<Response<OcrNpRespDTO>> faceOcrResult = doOcr(dto, personInfo.getFacePicBase64());

            //国徽ocr
            Future<Response<OcrNpRespDTO>> nationOcrResult = doOcr(dto, personInfo.getNationalEmblemPicBase64());

            //身份信息核查
            Future<Boolean> certCheckResult = doCertCheck(dto, personInfo.getFullName(), personInfo.getCertNumber());

            try {
                RnrResponseDTO.OcrResultInfo personOcrResult = processOcrResult(dto, personInfo,
                        faceOcrResult.get(5, TimeUnit.SECONDS),
                        nationOcrResult.get(5, TimeUnit.SECONDS), subjectType);

                Boolean certCheck = certCheckResult.get(5, TimeUnit.SECONDS);
                if (certCheck) {
                    recordRnrResultLog(dto, AuthWayTypeEnum.CERT_CHECK, "1", "身份信息核查成功", subjectType);
                } else {
                    recordRnrResultLog(dto, AuthWayTypeEnum.CERT_CHECK, "2", "身份信息核查失败", subjectType);
                }
                personOcrResult.setCertCheckResult(certCheck);

                ocrResultInfoList.add(personOcrResult);
            } catch (Exception e) {
                log.error("personListInfoProcess has error,fullName = {},certNumber = {}", personInfo.getFullName(),
                        personInfo.getCertNumber(), e);
            }
        }

        rnrResponseDTO.setPersonListOcrResult(ocrResultInfoList);
    }


    private void videoPersonInfoProcess(RnrRequestDTO dto, RnrResponseDTO rnrResponseDTO, String bestFrame) {
        RnrRequestDTO.RnrPersonInfo videoPersonInfo = dto.getMatchVideoPerosinInfo();
        if (!videoPersonInfo.getIsIdentityCard()) {
            //非身份证不需要做 ocr  人证比对 人脸比对
            return;
        }
        //ocr 做活体视频 人脸照
        Future<Response<OcrNpRespDTO>> facePicOcrFuture = doOcr(dto, videoPersonInfo.getFacePicBase64());

        //ocr 做活体视频 国徽照
        Future<Response<OcrNpRespDTO>> nationPicOcrFuture = doOcr(dto, videoPersonInfo.getNationalEmblemPicBase64());

        //人证比对
        Future<Response<IdCompareRespDTO>> idCompareFuture =
                doIdCompare(dto, bestFrame, videoPersonInfo.getCertNumber(), videoPersonInfo.getFullName());

        //人脸比对
        Future<Response<FaceCompareRespDTO>> faceCompareFuture =
                doFaceCompare(bestFrame, videoPersonInfo.getFacePicBase64(), dto);

        //判断是不是本人
        Integer subjectType = CollectionUtils.isEmpty(dto.getPeorsonList()) ? SubjectTypeEnum.ONESELF.getCode() :
                SubjectTypeEnum.CONSIGNOR.getCode();

        try {
            RnrResponseDTO.OcrResultInfo videoPersonOcrResult = processOcrResult(dto, videoPersonInfo,
                    facePicOcrFuture.get(5, TimeUnit.SECONDS),
                    nationPicOcrFuture.get(5, TimeUnit.SECONDS), subjectType);

            rnrResponseDTO.setVideoPerosonOcResult(videoPersonOcrResult);
        } catch (Exception e) {
            log.error("videoPersonInfoProcess ocr Future has error ,fullName = {},certNumber = {}",
                    videoPersonInfo.getFullName(), videoPersonInfo.getCertNumber(), e);
            recordRnrResultLog(dto, AuthWayTypeEnum.OCR, "2", "OCR异常", subjectType);

            RnrResponseDTO.OcrResultInfo videoPersonOcrResult = new RnrResponseDTO.OcrResultInfo();
            videoPersonOcrResult.setOcrFailMsg(Collections.singletonList("OCR异常:请求异常"));
            rnrResponseDTO.setVideoPerosonOcResult(videoPersonOcrResult);
        }

        try {
            Response<IdCompareRespDTO> response = idCompareFuture.get(5, TimeUnit.SECONDS);
            if (!response.isSuccess()) {
                recordRnrResultLog(dto, AuthWayTypeEnum.ID_COMPARE, "2", "人证比对失败,:" + response.getMsg(), subjectType);
                rnrResponseDTO.setIdCompareFailMsg("人证比对失败,:" + response.getMsg());
            } else {
                String similarity = response.getData().getSimilarity();
                if (Integer.parseInt(similarity) > Integer.parseInt(rnrFpConfig.getIdCompareSimilarity())) {
                    rnrResponseDTO.setIdCompareResult(true);
                    recordRnrResultLog(dto, AuthWayTypeEnum.ID_COMPARE, "1", "人证比对成功,相似度:" + similarity, subjectType);
                } else {
                    recordRnrResultLog(dto, AuthWayTypeEnum.ID_COMPARE, "2", "人证比对失败,相似度:" + similarity, subjectType);
                    rnrResponseDTO.setIdCompareFailMsg("人证比对失败,相似度:" + similarity);
                }
            }
        } catch (Exception e) {
            log.error("videoPersonInfoProcess idCompare Future has error,fullName = {},certNumber = {}",
                    videoPersonInfo.getFullName(), videoPersonInfo.getCertNumber(), e);
            recordRnrResultLog(dto, AuthWayTypeEnum.ID_COMPARE, "2", "人证比对异常:请求异常", subjectType);
            rnrResponseDTO.setIdCompareFailMsg("人证比对异常:请求异常");
        }

        try {
            Response<FaceCompareRespDTO> response = faceCompareFuture.get(5, TimeUnit.SECONDS);
            if (!response.isSuccess()) {
                recordRnrResultLog(dto, AuthWayTypeEnum.FACE_COMPARE, "2", "人脸比对失败:" + response.getMsg(), subjectType);
                rnrResponseDTO.setFaceCompareFailMsg("人脸比对失败:" + response.getMsg());
            } else {
                String similarity = response.getData().getSimilarity();
                if (Integer.parseInt(similarity) > Integer.parseInt(rnrFpConfig.getFaceCompareSimilarity())) {
                    rnrResponseDTO.setFaceCompareResult(true);
                    recordRnrResultLog(dto, AuthWayTypeEnum.FACE_COMPARE, "1", "人脸比对成功,相似度:" + similarity, subjectType);
                } else {
                    recordRnrResultLog(dto, AuthWayTypeEnum.FACE_COMPARE, "2", "人脸比对失败,相似度:" + similarity, subjectType);
                    rnrResponseDTO.setFaceCompareFailMsg("人脸比对失败,相似度:" + similarity);
                }
            }
        } catch (Exception e) {
            log.error("doRnr ,videoPersonInfoProcess faceCompare Future has error,fullName = {},certNumber = {}",
                    videoPersonInfo.getFullName(), videoPersonInfo.getCertNumber(), e);
            recordRnrResultLog(dto, AuthWayTypeEnum.FACE_COMPARE, "2", "人脸比对异常:请求异常", subjectType);
            rnrResponseDTO.setFaceCompareFailMsg("人脸比对异常:请求异常");
        }
    }

    private RnrResponseDTO.OcrResultInfo processOcrResult(RnrRequestDTO dto, RnrRequestDTO.RnrPersonInfo personInfo,
                                                          Response<OcrNpRespDTO> faceOcrResponse
            , Response<OcrNpRespDTO> nationResponse, Integer subjectType) {
        RnrResponseDTO.OcrResultInfo ocrResultInfo = new RnrResponseDTO.OcrResultInfo();

        List<String> failMsg = new ArrayList<>();
        if (!faceOcrResponse.isSuccess() || !nationResponse.isSuccess() || null == faceOcrResponse.getData()
                || null == nationResponse.getData()) {
            if (!faceOcrResponse.isSuccess()) {
                failMsg.add(faceOcrResponse.getMsg());
				recordRnrResultLog(dto, AuthWayTypeEnum.OCR, "2", faceOcrResponse.getMsg(), subjectType);
            }
            if (!nationResponse.isSuccess()) {
                failMsg.add(nationResponse.getMsg());
				recordRnrResultLog(dto, AuthWayTypeEnum.OCR, "2", faceOcrResponse.getMsg(), subjectType);
            }
            return ocrResultInfo;
        }
        OcrNpRespDTO faceData = faceOcrResponse.getData();
        OcrNpRespDTO nationData = nationResponse.getData();
        ocrResultInfo.setOrgianFullName(personInfo.getFullName());

        //填充ocr读取的信息
        ocrResultInfo.setOcrFullName(faceData.getFullName());
        ocrResultInfo.setOcrCertNumber(faceData.getCertNumber());
        ocrResultInfo.setOcrGender(faceData.getGender());
        ocrResultInfo.setOcrAddress(faceData.getAddress());
        ocrResultInfo.setOcrBirthday(faceData.getBirthday());
        ocrResultInfo.setOcrRace(faceData.getRace());
        ocrResultInfo.setOcrIssued(nationData.getIssued());
        ocrResultInfo.setOcrValidDate(nationData.getValidDate());

        boolean compareFullName = personInfo.getFullName().equals(faceData.getFullName());
        if (!compareFullName) {
            recordRnrResultLog(dto, AuthWayTypeEnum.OCR, "2", "ocr扫描结果身份证姓名与输入身份证信息不一致", subjectType);
            failMsg.add("ocr扫描结果身份证姓名与输入身份证信息不一致");
        }
        boolean certNumberCompare = personInfo.getCertNumber().equals(faceData.getCertNumber());
        if (!certNumberCompare) {
            recordRnrResultLog(dto, AuthWayTypeEnum.OCR, "2", "ocr扫描结果身份证号码与输入身份证信息不一致", subjectType);
            failMsg.add("ocr扫描结果身份证号码与输入身份证信息不一致");
        }

        boolean genderCompare =
                null != personInfo.getGender() && GenderEnum.findByCode(personInfo.getGender()).getName()
                        .equals(faceData.getGender());
        if (!genderCompare) {
            recordRnrResultLog(dto, AuthWayTypeEnum.OCR, "2", "ocr扫描结果性别与输入身份证信息不一致", subjectType);
            failMsg.add("ocr扫描结果性别与输入身份证信息不一致");
        }


        if (StringUtils.isNotBlank(nationData.getValidDate())) {
            //读取出来的有效期格式为2021.11.11-2022.12.12
            String validDate = nationData.getValidDate().split("-")[1];
//            Date validDate = DateUtil.parseDate(validDateStr, DateUtil.YYYYMMDDDOT);
            boolean validDateCheck = true;
            if (StringUtils.isBlank(validDate)) {
                recordRnrResultLog(dto, AuthWayTypeEnum.OCR, "2", "ocr扫描结果身份证有效期非法", subjectType);
                failMsg.add("ocr扫描结果身份证有效期非法");
                validDateCheck = false;
            }

            if (StringUtils.isNotBlank(validDate)) {
                //将截止时间统一改为-
                validDate = validDate.replaceAll("\\.", "-");

                if (!validDate.equals(personInfo.getExpiredDate())) {
                    recordRnrResultLog(dto, AuthWayTypeEnum.OCR, "2", "ocr扫描结果身份证有效期与输入身份证有效期不一致", subjectType);
                    log.info("processOcrResult validDate ：{}", validDate);
                    failMsg.add("ocr扫描结果身份证有效期与输入身份证有效期不一致");
                    validDateCheck = false;
                }

                if (!"长期".equals(validDate)) {
                    Date date = DateUtils.parseDate(validDate, "yyyy-MM-dd");
                    if (StringUtils.isNotBlank(validDate) && null != date
                            && date.getTime() < System.currentTimeMillis()) {
                        recordRnrResultLog(dto, AuthWayTypeEnum.OCR, "2", "ocr扫描结果身份证有效期失效", subjectType);
                        failMsg.add("ocr扫描结果身份证有效期失效");
                        validDateCheck = false;
                    }
                }
            }

            ocrResultInfo.setOcrResult(compareFullName && certNumberCompare && genderCompare && validDateCheck);

            if (Boolean.TRUE.equals(ocrResultInfo.getOcrResult())) {
                recordRnrResultLog(dto, AuthWayTypeEnum.OCR, "1", "ocr成功", subjectType);
            }
        } else {
            recordRnrResultLog(dto, AuthWayTypeEnum.OCR, "2", "ocr扫描结果身份证有效期为空", subjectType);
            failMsg.add("ocr扫描结果身份证有效期为空");
        }

        if (!CollectionUtils.isEmpty(failMsg)) {
            ocrResultInfo.setOcrFailMsg(failMsg);
        }

        return ocrResultInfo;
    }


    private Future<Response<FaceCompareRespDTO>> doFaceCompare(String bestFrame, String faceImg, RnrRequestDTO dto) {
        return rnrExecutorService.submit(() -> {
            FaceCompareReqDTO faceDto = new FaceCompareReqDTO();
            faceDto.setSerialNumber(dto.getSerialNumber());
            faceDto.setBizUuid(dto.getBizUuid());
            faceDto.setTenantNo(dto.getTenantNo());

            faceDto.setBaseImg(bestFrame);
            faceDto.setFaceImg(faceImg);
            Response<FaceCompareRespDTO> response =
                    cloudService.postForResponse(CloudMethod.FACE_COMPARE, faceDto, FaceCompareRespDTO.class);
            log.info("认证流程 , 人脸比对 rnrId = {},response = {}", dto.getBizUuid(), JSONObject.toJSONString(response));
            return response;
        });
    }

    private Future<Response<IdCompareRespDTO>> doIdCompare(RnrRequestDTO dto,
                                                           String bestFrame,
                                                           String certNumber,
                                                           String fullName) {
        return rnrExecutorService.submit(() -> {
            IdCompareReqDTO idDto = new IdCompareReqDTO();
            idDto.setSerialNumber(dto.getSerialNumber());
            idDto.setBizUuid(dto.getBizUuid());
            idDto.setTenantNo(dto.getTenantNo());

            idDto.setFaceImg(bestFrame);
            idDto.setCertNumber(certNumber);
            idDto.setFullName(fullName);
            Response<IdCompareRespDTO> response =
                    cloudService.postForResponse(CloudMethod.ID_COMPARE, idDto, IdCompareRespDTO.class);
            log.info("认证流程 , 人证比对 rnrId = {},response = {}", dto.getBizUuid(), JSONObject.toJSONString(response));
            return response;
        });
    }

    private Future<Response<OcrNpRespDTO>> doOcr(RnrRequestDTO dto, String picBase64) {
        return rnrExecutorService.submit(() -> {
            OcrNpReqDTO ocrDto = new OcrNpReqDTO();
            ocrDto.setSerialNumber(dto.getSerialNumber());
            ocrDto.setBizUuid(dto.getBizUuid());
            ocrDto.setTenantNo(dto.getTenantNo());

            ocrDto.setOcrPic(picBase64);
            Response<OcrNpRespDTO> ocr = cloudService.postForResponse(CloudMethod.OCR_NP, ocrDto, OcrNpRespDTO.class);
            System.out.println("认证流程 , 身份证ocr,response = {}" + JSONObject.toJSONString(ocr));
            log.info("认证流程 , 身份证ocr rnrId = {},response = {}", dto.getBizUuid(), JSONObject.toJSONString(ocr));
            return ocr;
        });
    }

    private Future<Boolean> doCertCheck(RnrRequestDTO dto, String fullName, String certNumber) {
        return rnrExecutorService.submit(() -> {
            CertCheckReqDTO certCheckDto = new CertCheckReqDTO();
            certCheckDto.setSerialNumber(dto.getSerialNumber());
            certCheckDto.setFullName(fullName);
            certCheckDto.setBizUuid(dto.getBizUuid());
            certCheckDto.setCertNumber(certNumber);
            Response rnrResponse =
                    cloudService.postForResponse(CloudMethod.CERT_CHECK, certCheckDto, LivenessCodeResponseDTO.class);
            log.info("认证流程, 身份信息核查,response = {}" + JSONObject.toJSONString(rnrResponse));
            log.info("认证流程, 身份信息核查 rnrId = {},response = {}", dto.getBizUuid(), JSONObject.toJSONString(rnrResponse));
            return rnrResponse.isSuccess();
        });
    }

    private String[] doLiveness(RnrRequestDTO dto, RnrResponseDTO rnrResponseDTO) {
        String livenessCode = null;
        try {
            livenessCode = cacheFactory.getStringService()
                    .getValue(RedisConstant.USER_RNR_LIVENESSCODE_PREFIX + dto.getRequestId(), String.class);
        } catch (Exception e) {
            log.error("getLivenessCode from redis has error , dto = {}", JSONObject.toJSONString(dto), e);
        }

        if (StringUtils.isBlank(livenessCode)) {
            rnrResponseDTO.setLivenessFailMsg("活体失败:未查询到活体数字验证码/动作序列");
            return null;
        }
        LivenessReqDTO livenessDto = new LivenessReqDTO();

        livenessDto.setSerialNumber(dto.getSerialNumber());
        livenessDto.setVideoBase64(dto.getVideoBase64());
        livenessDto.setLivenessType(rnrFpConfig.getLivenessType());
        livenessDto.setValidateData(livenessCode);

        Response<LivenessRespDTO> livenessResponse;
        try {
            //todo 调用云端接口
            livenessResponse = cloudService.postForResponse(CloudMethod.LIVENESS, livenessDto, LivenessRespDTO.class);
            log.info("认证流程 , 活体检测 rnrId = {},response = {}", dto.getBizUuid(), livenessResponse.getCode());
            if (!livenessResponse.isSuccess() || null == livenessResponse.getData()) {
                rnrResponseDTO.setLivenessFailMsg("活体失败:" + livenessResponse.getMsg());
                return null;
            }

        } catch (Exception e) {
            log.error("doLiveness has error", e);
            rnrResponseDTO.setLivenessFailMsg("活体失败:请求失败");
            return null;
        }

        LivenessRespDTO data = livenessResponse.getData();
        if (null == data.getBestFrameList() || data.getBestFrameList().length != 2) {
            rnrResponseDTO.setLivenessFailMsg("活体失败:活体认证返回的图片少于两张");
            return null;
        }
        rnrResponseDTO.setLivenessResult(true);
        rnrResponseDTO.setLivenessPic1(fileService.upLoadImgBase64(data.getBestFrameList()[0], "png"));
        rnrResponseDTO.setLivenessPic2(fileService.upLoadImgBase64(data.getBestFrameList()[1], "png"));
        Integer subjectType = CollectionUtils.isEmpty(dto.getPeorsonList()) ? SubjectTypeEnum.ONESELF.getCode() :
                SubjectTypeEnum.CONSIGNOR.getCode();
        recordRnrResultLog(dto, AuthWayTypeEnum.LIVENESS, "1", "活体成功", subjectType);
        return data.getBestFrameList();
    }

    /**
     * 审核页面重试接口
     *
     * @param dto
     * @return
     */
    @Override
    public RnrRequestDTO rnrPersonRelationDto2RnrRequestDTO(RnrRelationDTO dto) {

        RnrRequestDTO rnrRequestDTO = new RnrRequestDTO();
        rnrRequestDTO.setTenantNo(dto.getTenantNo());
        rnrRequestDTO.setBizUuid(dto.getInfo().getUuid());
        Map<String, Map<Integer, String>> fileMap = downLoadFileBase64FromRnrRelationDTO(dto);

        //是否是委托人实名
        boolean trust = dto.getIsTrust() == 1;

        MgRnrLiaisonInfoDTO consigneePersonInfo = null;
        if (trust) {
            //委托实名 被委托人做活体视频
            consigneePersonInfo = dto.getRnrLiaisonList().stream()
                    .filter(mgRnrLiaisonInfoDTO -> RnrLiaisonType.CONSIGNEE.getCode()
                            .equals(mgRnrLiaisonInfoDTO.getLiaisonType()))
                    .findFirst().orElseThrow(() -> new CuscUserException("", "未查询到代办人信息"));

        }
        Map<Integer, String> videoPersonFileInfo = fileMap.get(trust ? consigneePersonInfo.getUuid() : "0");
        if (null == videoPersonFileInfo) {
            throw new CuscUserException("", "未查询到" + (trust ? "代办人" : "本人") + "文件信息");
        }
        //获取活体视频 活体视频人的id为0
        String videoBase64 = videoPersonFileInfo.get(RnrFileType.LIVENESS_VIDEO.getCode());
        if (StringUtils.isBlank(videoBase64)) {
            throw new CuscUserException("", "未查询到" + (trust ? "代办人" : "本人") + "活体视频");
        }
        rnrRequestDTO.setVideoBase64(videoBase64);
        rnrRequestDTO.setRequestId(dto.getRequestId());

        //构建 活体视频对应的人的信息
        RnrRequestDTO.RnrPersonInfo videoPersonInfo = new RnrRequestDTO.RnrPersonInfo();
        if (trust) {
            //被委托人的信息
            videoPersonInfo.setPersonId(consigneePersonInfo.getUuid());
            videoPersonInfo.setCertNumber(consigneePersonInfo.getLiaisonCertNumber());
            videoPersonInfo.setFullName(consigneePersonInfo.getLiaisonName());
            videoPersonInfo.setGender(consigneePersonInfo.getLiaisonGender());
            videoPersonInfo.setExpiredDate(consigneePersonInfo.getLiaisonExpiredDate());
        } else {
            videoPersonInfo.setPersonId("0");
            videoPersonInfo.setCertNumber(dto.getInfo().getCertNumber());
            videoPersonInfo.setFullName(dto.getInfo().getFullName());
            videoPersonInfo.setExpiredDate(dto.getInfo().getExpiredDate());
            videoPersonInfo.setGender(dto.getInfo().getGender());
        }

        String idFaceBase64 = videoPersonFileInfo.get(RnrFileType.IDENTITY_CARD_BACK.getCode());
        if (StringUtils.isNotBlank(idFaceBase64)) {
            videoPersonInfo.setFacePicBase64(idFaceBase64);
        }
        String nationFaceBase64 = videoPersonFileInfo.get(RnrFileType.IDENTITY_CARD_FRONT.getCode());
        if (StringUtils.isNotBlank(idFaceBase64)) {
            videoPersonInfo.setNationalEmblemPicBase64(nationFaceBase64);
        }
        videoPersonInfo.setIsIdentityCard(StringUtils.isNotBlank(idFaceBase64) && StringUtils.isNotBlank(idFaceBase64));
        rnrRequestDTO.setMatchVideoPerosinInfo(videoPersonInfo);

        //构建其他人的信息
        List<RnrRequestDTO.RnrPersonInfo> personList = new ArrayList<>();

        if (trust) {
            //将委托人的信息 放在PeorsonList中
            RnrRequestDTO.RnrPersonInfo selfPerson = new RnrRequestDTO.RnrPersonInfo();
            selfPerson.setPersonId("");
            selfPerson.setFullName(dto.getInfo().getFullName());
            selfPerson.setCertNumber(dto.getInfo().getCertNumber());
            selfPerson.setExpiredDate(dto.getInfo().getExpiredDate());
            selfPerson.setGender(dto.getInfo().getGender());
            Map<Integer, String> selfFileMap = fileMap.get("0");
            String personIdFaceBase64 = selfFileMap.get(RnrFileType.IDENTITY_CARD_BACK.getCode());
            if (StringUtils.isNotBlank(personIdFaceBase64)) {
                selfPerson.setFacePicBase64(personIdFaceBase64);
            }
            String personNationFaceBase64 = selfFileMap.get(RnrFileType.IDENTITY_CARD_FRONT.getCode());
            if (StringUtils.isNotBlank(personNationFaceBase64)) {
                selfPerson.setNationalEmblemPicBase64(personNationFaceBase64);
            }
            selfPerson.setIsIdentityCard(
                    StringUtils.isNotBlank(personIdFaceBase64) && StringUtils.isNotBlank(personNationFaceBase64));
            personList.add(selfPerson);
        }

        rnrRequestDTO.setPeorsonList(personList);

        return rnrRequestDTO;
    }


    @Override
    public void personRnrH5(String serialNumber, RnrRelationDTO dto) {
        //卡实名状态校验
        if (dto.getIsSecondHandCar() == 0) {
            iccidListCheck(dto);
        }

        //一证十卡校验
        cardNumberCheck(dto);

        dto.getInfo().setRnrStatus(RnrStatus.INIT.getCode());
        dto.getInfo().setRnrBizzType(RnrBizzTypeEnum.Bind.getCode());
        dto.getOrder().setOrderStatus(RnrOrderStatusEnum.COMMIT.getCode());
        dto.getOrder().setRnrBizzType(RnrBizzTypeEnum.Bind.getCode());
        dto.getOrder().setSendWorkOrder(false);
        dto.getCardList().forEach(card -> card.setRnrStatus(RnrStatus.INIT.getCode()));
		Response response = mgRnrRelationClient.saveRnrRelation(dto);
		if (response.isSuccess()) {
			RnrOrderDTO message = new RnrOrderDTO();
			message.setUuid(dto.getOrder().getUuid());
			changeOrUnbindIccidsClient.sendMSMToMQ(message);
		}
	}


    @Override
    public Integer personH5CallBack(PersonH5CallBackRequestDTO dto) {
        if (null == dto.getLivenessPicFileId() || dto.getLivenessPicFileId().length != 2) {
            throw new CuscUserException("", "活体照片不是2张");
        }
        //订单号
        String orderNo = dto.getOrderNo();

        //根据订单号加锁
        try {
            boolean lockStatus = cacheFactory.getLockService().lock(RedisConstant.RNR_H5_CALLBACK_LOCK + orderNo,
                    RedisConstant.RNR_H5_CALLBACK_LOCK_EXPIRE);
            if(!lockStatus){
                throw new CuscUserException("", "当前有正在处理的实名请求，请勿重复提交");
            }
        } catch (CacheException e) {
            log.error("personH5CallBack 加锁失败 ", e);
        }

        RnrOrderDTO orderQuery = new RnrOrderDTO();
        orderQuery.setUuid(orderNo);
        Response<RnrOrderDTO> orderResponse = orderClient.getByUuid(orderQuery);
        if (!orderResponse.isSuccess() || null == orderResponse.getData()) {
            throw new CuscUserException("", "未查询到订单信息");
        }
        RnrOrderDTO orderDTO = orderResponse.getData();
        //判断工单状态，如果是审核通过则不继续
        if (orderDTO.getOrderStatus() != null
                && RnrOrderStatusEnum.PASS.getCode().intValue() == orderDTO.getOrderStatus().intValue()) {
            return RnrResultEnum.PASS.getCode();
        }
        //判断工单是否作废
        if(orderDTO.getOrderStatus() != null && RnrOrderStatusEnum.CANCEL.getCode().intValue() == orderDTO.getOrderStatus().intValue()){
            throw new CuscUserException("","实名工单已超时作废，请重新实名");
        }

        if(orderDTO.getOrderStatus() != null && RnrOrderStatusEnum.NOT_PASS.getCode().intValue() == orderDTO.getOrderStatus().intValue()){
            throw new CuscUserException("","实名工单审核不通过，请重新实名");
        }

        //判断工单状态，如果是审核通过则不继续
        if (orderDTO.getOrderStatus() != null
                && RnrOrderStatusEnum.PASS.getCode().intValue() == orderDTO.getOrderStatus().intValue()) {
            return RnrResultEnum.PASS.getCode();
        }

        String rnrId = orderDTO.getRnrId();
        MgRnrInfoDTO rnrInfoQuery = new MgRnrInfoDTO();
        rnrInfoQuery.setUuid(rnrId);
        Response<MgRnrInfoDTO> rnrInfoResponse = rnrInfoClient.getByUuid(rnrInfoQuery);
        if (!rnrInfoResponse.isSuccess() || null == rnrInfoResponse.getData()) {
            throw new CuscUserException("", "未查询到实名信息");
        }
        MgRnrInfoDTO rnrInfo = rnrInfoResponse.getData();

        if (orderDTO.getOrderStatus() != null && RnrOrderStatusEnum.ASSIGNMENT.getCode().intValue() == orderDTO.getOrderStatus().intValue()) {
            throw new CuscUserException("", "已有工单在审核中,请等待");
        }
        //H5认证的信息 构建 认证流程请求
        RnrResponseDTO rnrResponse;

        String[] bestBaseLiveness64 = new String[2];
        String[] bestBaseLiveness64ForFile = new String[2];


        AttachmentDTO attachmentDTO = new AttachmentDTO();
        attachmentDTO.setSerialNumber(CuscStringUtils.generateUuid());
        attachmentDTO.setFileId(dto.getLivenessPicFileId()[0]);
        Response<AttachmentRespDTO> response1 = cloudService.postForResponse(CloudMethod.ATTACHMENT_DOWNLOAD,
                attachmentDTO, AttachmentRespDTO.class);
        if (response1.isSuccess()) {
            AttachmentRespDTO attachmentRespDTO = response1.getData();
            bestBaseLiveness64[0] = attachmentRespDTO.getBase64();
            bestBaseLiveness64[1] = attachmentRespDTO.getBase64();
            bestBaseLiveness64ForFile[0] = attachmentRespDTO.getBase64();
        } else {
            throw new CuscUserException(ResponseCode.SYSTEM_ERROR.getCode(), "下载活体截图失败");
        }


        AttachmentDTO attachmentDTOForVideo = new AttachmentDTO();
        attachmentDTOForVideo.setSerialNumber(CuscStringUtils.generateUuid());
        attachmentDTOForVideo.setFileId(dto.getVideFileId());
        Response<AttachmentRespDTO> response3 = cloudService.postForResponse(CloudMethod.ATTACHMENT_DOWNLOAD,
                attachmentDTOForVideo, AttachmentRespDTO.class);
        if (response3.isSuccess()) {
            AttachmentRespDTO attachmentRespDTO = response3.getData();
            bestBaseLiveness64ForFile[1] = attachmentRespDTO.getBase64();
        } else {
            log.info("活体视频下载失败,orderNo = {}", dto.getOrderNo());
        }
		//是否是代办人模式
		boolean trust = rnrInfo.getIsTrust() == 1;
        RnrRequestDTO rnrRequestDTO = h5InfoToRnrRequest(trust,orderDTO, rnrId, rnrInfo);
        rnrResponse = doPersonRnr(rnrRequestDTO, bestBaseLiveness64);




        //非二手车且只是身份证时，自然人实名失败则自动返回
        RnrReturnDTO personRnrMsg = personRnrResult(rnrInfo, rnrResponse);
        if ( null != personRnrMsg) {
            //通过实名id查询vin
            MgRnrCardInfoDTO cardInfoDTO = new MgRnrCardInfoDTO();
            cardInfoDTO.setRnrId(rnrInfo.getUuid());
            cardInfoDTO.setTenantNo(rnrInfo.getTenantNo());
            Response<List<MgRnrCardInfoDTO>> cardResp = rnrCardInfoClient.queryByList(cardInfoDTO);
            if (!cardResp.isSuccess()) {
                throw new CuscUserException(cardResp.getCode(), cardResp.getMsg());
            }
            if (CollectionUtils.isEmpty(cardResp.getData())) {
                throw new CuscUserException("", "未查询到实名卡信息");
            }
            String errorCountKey = rnrInfo.getCertNumber() + "_" + cardResp.getData().get(0).getIotId();
            //检查实名错误次数是否超过配置次数
            if (!toArtificialOrder(errorCountKey)) {
                //设置错误次数
                setPersonRnrErrorCount(errorCountKey);
                updateInValid(rnrId, orderNo,StringUtils.isNotBlank(personRnrMsg.getPersonRnrResultMsg())?personRnrMsg.getPersonRnrResultMsg():personRnrMsg.getPersonH5CallBackMsg());
                throw new CuscUserException("", personRnrMsg.getPersonRnrResultMsg());
            }
        }

        if (rnrResponse.getRnrResult() && rnrInfo.getIsSecondHandCar() == 0 && rnrInfo.getIsTrust() == 0) {
            String userId = null;
            try {
                if (CuscStringUtils.isEmpty(dto.getCiamUserId()) && !RnrOrderSourceEnum.H5_CUSTOMER.getCode().equals(orderDTO.getOrderSource())) {
                    CiamUserDTO ciamUserDto = new CiamUserDTO();
                    ciamUserDto.setTenantNo(rnrInfo.getTenantNo());
                    ciamUserDto.setPhoneNum(rnrInfo.getPhone());
                    Response<CiamUserDTO> user = ciamUserClient.createUser(ciamUserDto);
                    if (null != user && user.isSuccess() && null != user.getData()) {
                        userId = user.getData().getUuid();
                    }
                }
            } catch (Exception e) {
                log.error("H5实名成功 创建C端用户 错误 dto = {}", JSONObject.toJSONString(dto), e);
            }

            processRnrResult(orderDTO, true, RnrOrderStatusEnum.PASS, userId,StringUtils.isNotEmpty(personRnrMsg.getPersonRnrResultMsg())?personRnrMsg.getPersonRnrResultMsg():personRnrMsg.getPersonH5CallBackMsg());
        } else {
            processRnrResult(orderDTO, false, RnrOrderStatusEnum.ASSIGNMENT, null,StringUtils.isNotEmpty(personRnrMsg.getPersonRnrResultMsg())?personRnrMsg.getPersonRnrResultMsg():personRnrMsg.getPersonH5CallBackMsg());
            try {
                //发起一个工单
                RnrOrderDTO workOrder = new RnrOrderDTO();
                workOrder.setUuid(orderDTO.getUuid());
                orderClient.startWorkOrder(workOrder);
            } catch (Exception e) {
                log.error("H5回调发起工单流程失败:info = {}", JSONObject.toJSONString(dto), e);
            }
        }

        //文件上传
        String best64Img = bestBaseLiveness64ForFile[0];
        String best64ImgFileKey = "";
        if (StringUtils.isNotBlank(best64Img)) {
            best64ImgFileKey = fileService.upLoadImgBase64(best64Img, "png");
        }

        String video = bestBaseLiveness64ForFile[1];
        String videoFileKey = "";
        if (StringUtils.isNotBlank(video)) {
            videoFileKey = fileService.upLoadFileBase64(video, "mp4");
        }

		String liaisonId = rnrRequestDTO.getMatchVideoPerosinInfo().getPersonId();
        //视屏文件存储
        MgRnrFileDTO videoFile = new MgRnrFileDTO();
        videoFile.setUuid(CuscStringUtils.generateUuid());
        videoFile.setLiaisonId(liaisonId);
        videoFile.setRnrId(rnrId);
        videoFile.setFileType(RnrFileType.LIVENESS_VIDEO.getCode());
        videoFile.setFileSystemId(videoFileKey);
        videoFile.setTenantNo(orderDTO.getTenantNo());
        videoFile.setRoutingKey(orderDTO.getRoutingKey());
        videoFile.setRnrBizzType(RnrBizzTypeEnum.Bind.getCode());
        rnrFileClient.add(videoFile);

        //活体截图1
        MgRnrFileDTO faceFile = new MgRnrFileDTO();
        faceFile.setUuid(CuscStringUtils.generateUuid());
		faceFile.setLiaisonId(liaisonId);
        faceFile.setRnrId(rnrId);
        faceFile.setFileType(RnrFileType.LIVENESS_SCREEN_FIRST.getCode());
        faceFile.setFileSystemId(best64ImgFileKey);
        faceFile.setTenantNo(orderDTO.getTenantNo());
        faceFile.setRoutingKey(orderDTO.getRoutingKey());
        faceFile.setRnrBizzType(RnrBizzTypeEnum.Bind.getCode());
        rnrFileClient.add(faceFile);

        //活体截图1
        MgRnrFileDTO nationFile = new MgRnrFileDTO();
        nationFile.setUuid(CuscStringUtils.generateUuid());
		nationFile.setLiaisonId(liaisonId);
        nationFile.setRnrId(rnrId);
        nationFile.setFileType(RnrFileType.LIVENESS_SCREEN_TWO.getCode());
        nationFile.setFileSystemId(best64ImgFileKey);
        nationFile.setTenantNo(orderDTO.getTenantNo());
        nationFile.setRoutingKey(orderDTO.getRoutingKey());
        nationFile.setRnrBizzType(RnrBizzTypeEnum.Bind.getCode());
        rnrFileClient.add(nationFile);

        try {
            //解锁
            cacheFactory.getLockService().unLock(RedisConstant.RNR_H5_CALLBACK_LOCK + orderNo);
        } catch (CacheException e) {
            log.error("personH5CallBack 解锁失败 ", e);
        }

        return rnrResponse.getRnrResult() && rnrInfo.getIsTrust() == 0 && rnrInfo.getIsSecondHandCar() == 0 ? RnrResultEnum.PASS.getCode() :
                RnrResultEnum.REVIEW.getCode();
    }

    @Override
    public RnrRequestDTO h5InfoToRnrRequest(boolean isTrust,RnrOrderDTO orderDTO, String rnrId, MgRnrInfoDTO rnrInfo) {
		//认证请求
		RnrRequestDTO rnrRequestDTO = new RnrRequestDTO();
		rnrRequestDTO.setBizUuid(rnrId);
		rnrRequestDTO.setSerialNumber(orderDTO.getUuid());
		rnrRequestDTO.setTenantNo(rnrInfo.getTenantNo());

		//查询委托人信息
		MgRnrLiaisonInfoDTO liaisonInfoDTO = null;
		if (isTrust) {
			MgRnrLiaisonInfoDTO liaisonQuery = new MgRnrLiaisonInfoDTO();
			liaisonQuery.setRnrId(rnrId);
			liaisonInfoDTO = liaisonInfoClient.getByRnrid(liaisonQuery).getData();
			if (null == liaisonInfoDTO) {
				throw new CuscUserException(ResponseCode.SYSTEM_ERROR.getCode(), "未查询到责任人信息");
			}
		}

        //查询出文件
        MgRnrFileDTO rnrFileQuery = new MgRnrFileDTO();
        rnrFileQuery.setTenantNo(orderDTO.getTenantNo());
        rnrFileQuery.setRnrId(rnrId);
        Response<List<MgRnrFileDTO>> fileResponse = rnrFileClient.queryByList(rnrFileQuery);
        if (!fileResponse.isSuccess() && CollectionUtils.isEmpty(fileResponse.getData())) {
            throw new CuscUserException("", "未查询到文件信息");
        }

        List<MgRnrFileDTO> fileDTOList = fileResponse.getData();
        //实名本人的文件信息
        List<MgRnrFileDTO> rnrInfoFileList =
                fileDTOList.stream().filter(file -> file.getLiaisonId().equals("0")).collect(Collectors.toList());
        Map<Integer, MgRnrFileDTO> rnrFileMap = rnrInfoFileList.stream()
                .collect(Collectors.toMap(MgRnrFileDTO::getFileType, Function.identity(), (k, v) -> v));

		//委托人文件信息
		List<MgRnrFileDTO> trustInfoFileList =
				fileDTOList.stream().filter(file -> !file.getLiaisonId().equals("0")).collect(Collectors.toList());
		Map<Integer, MgRnrFileDTO> trustInfoFileMap = trustInfoFileList.stream()
				.collect(Collectors.toMap(MgRnrFileDTO::getFileType, Function.identity(), (k, v) -> v));


		//本人信息
		RnrRequestDTO.RnrPersonInfo selfPersonInfo = new RnrRequestDTO.RnrPersonInfo();
		selfPersonInfo.setPersonId("0");
		selfPersonInfo.setIsIdentityCard(CertTypeEnum.IDCARD.getCode().equals(rnrInfo.getCertType()));


        //身份证人像面
        MgRnrFileDTO faceFileDTO = rnrFileMap.get(RnrFileType.IDENTITY_CARD_BACK.getCode());

        //身份证国徽面
        MgRnrFileDTO nationFileDTO = rnrFileMap.get(RnrFileType.IDENTITY_CARD_FRONT.getCode());

		if (null != faceFileDTO) {
			String faceBase64 = fileService.downLoadBase64(faceFileDTO.getFileSystemId());
			selfPersonInfo.setFacePicBase64(faceBase64);
		}

		if (null != nationFileDTO) {
			String nationBase64 = fileService.downLoadBase64(nationFileDTO.getFileSystemId());
			selfPersonInfo.setNationalEmblemPicBase64(nationBase64);
		}

		selfPersonInfo.setCertNumber(rnrInfo.getCertNumber());
		selfPersonInfo.setFullName(rnrInfo.getFullName());
		selfPersonInfo.setExpiredDate(rnrInfo.getExpiredDate());
		selfPersonInfo.setGender(rnrInfo.getGender());

		//委托人信息
		RnrRequestDTO.RnrPersonInfo trustInfo = null;
		if (isTrust) {
			trustInfo = new RnrRequestDTO.RnrPersonInfo();
			trustInfo.setPersonId(liaisonInfoDTO.getUuid());
			trustInfo.setIsIdentityCard(CertTypeEnum.IDCARD.getCode().equals(liaisonInfoDTO.getLiaisonCertType()));

			//委托人身份证人像面
			MgRnrFileDTO trustFaceFileDTO = trustInfoFileMap.get(RnrFileType.IDENTITY_CARD_BACK.getCode());
			//委托人身份证国徽面
			MgRnrFileDTO trustNationFileDTO = trustInfoFileMap.get(RnrFileType.IDENTITY_CARD_FRONT.getCode());

			if (null != trustFaceFileDTO) {
				String faceBase64 = fileService.downLoadBase64(trustFaceFileDTO.getFileSystemId());
				trustInfo.setFacePicBase64(faceBase64);
			}
			if (null != trustNationFileDTO) {
				String nationBase64 = fileService.downLoadBase64(trustNationFileDTO.getFileSystemId());
				trustInfo.setNationalEmblemPicBase64(nationBase64);
			}
			trustInfo.setCertNumber(liaisonInfoDTO.getLiaisonCertNumber());
			trustInfo.setFullName(liaisonInfoDTO.getLiaisonName());
			trustInfo.setExpiredDate(liaisonInfoDTO.getLiaisonExpiredDate());
			trustInfo.setGender(liaisonInfoDTO.getLiaisonGender());
		}

		if (!isTrust) {
			rnrRequestDTO.setMatchVideoPerosinInfo(selfPersonInfo);
		} else {
			rnrRequestDTO.setMatchVideoPerosinInfo(trustInfo);
			rnrRequestDTO.setPeorsonList(Collections.singletonList(selfPersonInfo));
		}
		return rnrRequestDTO;
    }


    private void processRnrResult(RnrOrderDTO rnrOrderDTO, Boolean success, RnrOrderStatusEnum orderStatusEnum,
                                  String userId, String msg) {
        String rnrId = rnrOrderDTO.getRnrId();
        //更新工单信息
        RnrOrderDTO orderUpdate = new RnrOrderDTO();
        orderUpdate.setUuid(rnrOrderDTO.getUuid());
        orderUpdate.setOrderStatus(orderStatusEnum.getCode());
        if(StringUtils.isNotEmpty(msg)){
            orderUpdate.setComment(msg);
        }
        orderUpdate.setComment(msg);
        orderClient.update(orderUpdate);



        //更新实名信息
        if (success) {

			MgRnrInfoDTO rnrInfoUpdate = new MgRnrInfoDTO();
			rnrInfoUpdate.setUuid(rnrId);
			rnrInfoUpdate.setRnrStatus(RnrStatus.RNR.getCode());
			if (StringUtils.isNotBlank(userId)) {
				rnrInfoUpdate.setUserId(userId);
			}
			rnrInfoClient.update(rnrInfoUpdate);

			//更新卡信息
			MgRnrCardInfoDTO cardUpdate = new MgRnrCardInfoDTO();
			cardUpdate.setRnrId(rnrId);
			cardUpdate.setRnrStatus(RnrStatus.RNR.getCode());
			mgRnrCardInfoClient.updateCardStatusByRnrId(cardUpdate);

            try {
                MgRnrCardInfoDTO noticeDto = new MgRnrCardInfoDTO();
                noticeDto.setOrderId(rnrOrderDTO.getUuid());
                Response response = mgRnrCardInfoClient.sendCardNotice(noticeDto);
                log.info("H5实名成功 发送卡通知 dto = {} response = {}", JSONObject.toJSONString(rnrOrderDTO),
                        JSONObject.toJSONString(response));
            } catch (Exception e) {
                log.error("H5实名成功 发送卡通知 错误 dto = {}", JSONObject.toJSONString(rnrOrderDTO), e);
            }
        }

    }


    private void recordRnrResultLog(RnrRequestDTO rnrRequestDTO, AuthWayTypeEnum authWayTypeEnum,
                                    String authResult, String authResultMsg, Integer subjectType) {
        try {
            MgRnrAuthenticationResultDTO dto = new MgRnrAuthenticationResultDTO();
            dto.setRnrId(rnrRequestDTO.getBizUuid());
            dto.setOrderId(rnrRequestDTO.getSerialNumber());
            dto.setAuthWayType(authWayTypeEnum.getCode());
            dto.setTenantNo(rnrRequestDTO.getTenantNo());
            dto.setAuthResult(authResult);
            dto.setAuthResultMsg(authResultMsg);
            dto.setSubjectType(subjectType);
            Response response = authenticationResultClient.add(dto);
            log.info("recordRnrResultLog | request = {},response = {}", JSONObject.toJSONString(dto),
                    JSONObject.toJSONString(response));
        } catch (Exception e) {
            log.error(
                    "recordRnrResultLog has error,rnrId = {},orderId = {},authWayTypeEnum = {},authResult = {},"
                            + "authResultMsg = {}",
                    rnrRequestDTO.getBizUuid(), rnrRequestDTO.getSerialNumber(), authWayTypeEnum.getName(), authResult,
                    authResultMsg);
        }
    }

    /**
     * h5验证身份证信息
     *
     * @param dto
     * @return
     */
    @Override
    public Response<RnrResponseDTO> h5ValidationOCR(H5ValidationOCRReqDTO dto) {
        RnrResponseDTO returnBean = new RnrResponseDTO();
        List<RnrResponseDTO.OcrResultInfo> ocrResultInfoList = new ArrayList<>();
        Integer subjectType = 1;
        RnrRequestDTO rnrRequestDTO = new RnrRequestDTO();
        rnrRequestDTO.setTenantNo(dto.getTenantNo());
        rnrRequestDTO.setBizUuid(dto.getBizUuid());
        rnrRequestDTO.setSerialNumber(dto.getSerialNumber());

        String facePicBase64 = fileService.downLoadBase64(dto.getFacePicBase64());
        String nationalEmblemPicBase64 = fileService.downLoadBase64(dto.getNationalEmblemPicBase64());

        RnrRequestDTO.RnrPersonInfo personInfo = new RnrRequestDTO.RnrPersonInfo();
        personInfo.setPersonId(dto.getPersonId());
        personInfo.setIsIdentityCard(dto.getIsIdentityCard());
        personInfo.setFacePicBase64(facePicBase64);
        personInfo.setNationalEmblemPicBase64(nationalEmblemPicBase64);
        personInfo.setCertNumber(dto.getCertNumber());
        personInfo.setFullName(dto.getFullName());
        personInfo.setExpiredDate(dto.getExpiredDateStr());
        personInfo.setGender(dto.getGender());
        List<RnrRequestDTO.RnrPersonInfo> peorsonList = new ArrayList<>();
        peorsonList.add(personInfo);

        rnrRequestDTO.setPeorsonList(peorsonList);

        //人脸ocr
        Future<Response<OcrNpRespDTO>> faceOcrResult = doOcr(rnrRequestDTO, personInfo.getFacePicBase64());
        //国徽ocr
        Future<Response<OcrNpRespDTO>> nationOcrResult = doOcr(rnrRequestDTO, personInfo.getNationalEmblemPicBase64());
        //身份信息核查
        Future<Boolean> certCheckResult =
                doCertCheck(rnrRequestDTO, personInfo.getFullName(), personInfo.getCertNumber());
        try {
            RnrResponseDTO.OcrResultInfo personOcrResult = processOcrResult(rnrRequestDTO,
                    personInfo, faceOcrResult.get(5, TimeUnit.SECONDS),
                    nationOcrResult.get(5, TimeUnit.SECONDS), subjectType);
            Boolean certCheck = certCheckResult.get(5, TimeUnit.SECONDS);
            if (certCheck) {
                recordRnrResultLog(rnrRequestDTO, AuthWayTypeEnum.CERT_CHECK, "1", "身份信息核查成功", subjectType);
            } else {
                recordRnrResultLog(rnrRequestDTO, AuthWayTypeEnum.CERT_CHECK, "2", "身份信息核查失败", subjectType);
            }
            personOcrResult.setCertCheckResult(certCheck);
            ocrResultInfoList.add(personOcrResult);
        } catch (Exception e) {
            log.error("h5ValidationOCR has error,fullName = {},certNumber = {}", personInfo.getFullName(),
                    personInfo.getCertNumber(), e);
        }
        returnBean.setPersonListOcrResult(ocrResultInfoList);
        return Response.createSuccess(returnBean);
    }

    @Override
    public Response updateRnrInValid(RnrOrderDTO bean) {
        if (CuscStringUtils.isEmpty(bean.getUuid())) {
            return Response.createError("工单id不能为空");
        }
        Response<RnrOrderDTO> orderResp = orderClient.getByUuid(bean);
        if (!orderResp.isSuccess()) {
            return Response.createError(orderResp.getMsg(), orderResp.getCode());
        }
        if (orderResp.getData() == null) {
            return Response.createError("未查询到实名工单信息");
        }
        updateInValid(orderResp.getData().getRnrId(), orderResp.getData().getUuid(),"");
        return Response.createSuccess();
    }


    /**
     * 获取H5活体检测URL
     *
     * @param dto
     * @return
     */
    @Override
    public Response<LivenessResultDTO> h5LivenessResult(LiveLoginReqDTO dto) {
        return cloudService.postForResponse(CloudMethod.GET_LIVENESSRESULT, dto, LivenessResultDTO.class);
    }

    //--------------------私有方法区域--------------

    /**
     * Description: 自然人实名结果
     * <br />
     * CreateDate 2022-06-23 20:03:01
     **/
    private RnrReturnDTO personRnrResult(MgRnrInfoDTO rnrInfo, RnrResponseDTO rnrResponse) {
        RnrReturnDTO rnrReturnDTO = new RnrReturnDTO();
        log.info("personH5CallBack personRnrToArtificial : {}", JSONObject.toJSONString(rnrResponse));
        if (rnrInfo.getIsSecondHandCar() == 0 && CertTypeEnum.IDCARD.getCode().equals(rnrInfo.getCertType())) {
            if (rnrResponse.getVideoPerosonOcResult() != null && !Boolean.TRUE.equals(
                    rnrResponse.getVideoPerosonOcResult().getOcrResult())) {
                rnrReturnDTO.setPersonRnrResultMsg("ocr比对失败，请核验姓名、证件号、有效期、性别是否正确");
                return rnrReturnDTO;
            }
            if (!CollectionUtils.isEmpty(rnrResponse.getPersonListOcrResult())) {
                log.info("personH5CallBack ocr : {}", JSONObject.toJSONString(rnrResponse.getPersonListOcrResult()));
                for (RnrResponseDTO.OcrResultInfo ori : rnrResponse.getPersonListOcrResult()) {
                    if (ori.getOcrResult() == null || !ori.getOcrResult()) {
                        List<String> ocrFailMsg = ori.getOcrFailMsg();
                        if(CollectionUtils.isEmpty(ocrFailMsg)){
                            rnrReturnDTO.setPersonRnrResultMsg("ocr比对失败，请核验姓名、证件号、有效期、性别是否正确");
                        }else {
                            rnrReturnDTO.setPersonRnrResultMsg(String.join(",",ocrFailMsg));
                        }
                        return rnrReturnDTO;

                    }
                }
            }
            if (rnrResponse.getIdCompareResult() == null || !rnrResponse.getIdCompareResult()) {
                rnrReturnDTO.setPersonH5CallBackMsg(rnrResponse.getIdCompareFailMsg());

                return rnrReturnDTO;
            }
            if (rnrResponse.getFaceCompareResult() == null || !rnrResponse.getFaceCompareResult()) {
                rnrReturnDTO.setPersonH5CallBackMsg(rnrResponse.getFaceCompareFailMsg());
                return rnrReturnDTO;
            }
        }
        return null;
    }

    /**
     * Description: 是否转人工   true 转人工   false 不转人工
     * <br />
     * CreateDate 2022-06-23 20:03:01
     **/
    private boolean toArtificialOrder(String key) {
        try {
            Integer rnrErrorCount = cacheFactory.ExpireString()
                    .getValue(RedisConstant.PERSON_RNR_ERROR_COUNT_THRESHOLD + key, Integer.class);
            if (rnrErrorCount == null) {
                rnrErrorCount = 0;
            }
            if (rnrFpConfig.getPersonErrorCount().intValue() > rnrErrorCount.intValue()) {
                return false;
            }
        } catch (CacheException e) {
            log.error("toArtificialOrder error ", e);
        }
        return true;
    }

    /**
     * Description: 设置自然人实名错误次数
     * <br />
     * CreateDate 2022-06-23 20:03:01
     **/
    private void setPersonRnrErrorCount(String key) {
        try {
            Integer rnrErrorCount = cacheFactory.ExpireString()
                    .getValue(RedisConstant.PERSON_RNR_ERROR_COUNT_THRESHOLD + key, Integer.class);
            if (rnrErrorCount == null) {
                rnrErrorCount = 0;
            }
            rnrErrorCount = rnrErrorCount + 1;
            cacheFactory.ExpireString()
                    .setExpireValue(RedisConstant.PERSON_RNR_ERROR_COUNT_THRESHOLD + key, rnrErrorCount,
                            RedisConstant.PERSON_RNR_ERROR_COUNT_THRESHOLD_EXPIRE);
        } catch (CacheException e) {
            log.error("setPersonRnrErrorCount error ", e);
        }
    }

    /**
     * Description: 修改实名信息未无效
     * <br />
     * CreateDate 2022-06-25 15:07:53
     **/
    private void updateInValid(String rnrId, String orderUuid,String msg) {
        MgRnrInfoDTO rnrInfoUpdate = new MgRnrInfoDTO();
        rnrInfoUpdate.setUuid(rnrId);
        rnrInfoUpdate.setRnrStatus(RnrStatus.RNR_FAIL.getCode());
        rnrInfoClient.update(rnrInfoUpdate);

        RnrOrderDTO orderUpdate = new RnrOrderDTO();
        orderUpdate.setUuid(orderUuid);
        orderUpdate.setOrderStatus(RnrOrderStatusEnum.CANCEL.getCode());
        if(StringUtils.isNotEmpty(msg)) {
            orderUpdate.setComment(msg);
        }
        orderClient.update(orderUpdate);

        MgRnrCardInfoDTO cardUpdate = new MgRnrCardInfoDTO();
        cardUpdate.setRnrId(rnrId);
        cardUpdate.setRnrStatus(RnrStatus.RNR_FAIL.getCode());
        mgRnrCardInfoClient.updateCardStatusByRnrId(cardUpdate);
    }

}
