package cn.pluss.platform.ysExtension.v20220615;

import cn.pluss.platform.IdCardService;
import cn.pluss.platform.entity.*;
import cn.pluss.platform.exception.MsgException;
import cn.pluss.platform.jft.JftAuditService;
import cn.pluss.platform.jft.JftBankCardService;
import cn.pluss.platform.jft.JftMercBaseInfoService;
import cn.pluss.platform.jft.JftMerchantRateService;
import cn.pluss.platform.mapper.BankCodeYsMapper;
import cn.pluss.platform.mapper.JftMercPaymentChannelMapper;
import cn.pluss.platform.mcc.MccInfoSortService;
import cn.pluss.platform.mcc.MccInfoYsService;
import cn.pluss.platform.mcc.MccReflectService;
import cn.pluss.platform.merchant.AccountService;
import cn.pluss.platform.merchant.JftMercPaymentChannelService;
import cn.pluss.platform.notice.NoticeService;
import cn.pluss.platform.region.RegionReflectService;
import cn.pluss.platform.ryx.RyxConstants;
import cn.pluss.platform.user.impl.GeneralPushUtil;
import cn.pluss.platform.userApp.UserAppService;
import cn.pluss.platform.userInfo.UserInfoService;
import cn.pluss.platform.util.*;
import cn.pluss.platform.yinsheng.v20210929.ReqMethod;
import cn.pluss.platform.yinsheng.v20210929.RespEntity;
import cn.pluss.platform.ys.YSConstants;
import cn.pluss.platform.ys.impl.v20210929.YsConfigV2;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.MultiValueMap;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ExecutorService;

/**
 * @author djh
 */
@Slf4j
@Service("ysAuditServiceExtensionV2")
public class YsAuditServiceImpl extends AbstractYsResultService {

    @Autowired
    private GeneralPushUtil generalPushUtil;

    @Autowired
    private AccountService accountService;

    @Autowired
    private JftBankCardService jbcService;

    @Autowired
    private JftMercPaymentChannelService mpcService;

    @Autowired
    private IdCardService idCardService;

    @Autowired
    private RegionReflectService regionReflectService;

    @Autowired
    private MccReflectService mccReflectService;

    @Autowired
    private MccInfoYsService mccInfoYsService;

    @Resource
    private BankCodeYsMapper bankCodeYsMapper;

    @Autowired
    private MccInfoSortService mccInfoSortService;

    @Autowired
    private YsServiceV2 ysServiceV2;

    @Autowired
    private YsConfigExtensionV2 ysConfig;

    @Autowired
    private NoticeService noticeService;

    @Autowired
    private UserInfoService uiService;

    @Autowired
    private UserAppService uaService;

    @Autowired
    private YsAuditServiceImpl ysAuditServiceExtensionV2;

    @Autowired
    private ExecutorService executorService;

    @Autowired
    private JftAuditService jftAuditService;

    @Resource
    private JftMercPaymentChannelMapper jpcMapper;

    @Autowired
    private JftMerchantRateService jftMerchantRateService;

    @Autowired
    private JftMercBaseInfoService jmbiService;

    @Autowired
    private TransactionTemplate transactionTemplate;


    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void merchantAudit(String userId, boolean isFailCheck) {
        preCheck(userId);
        try {
            ysAuditServiceExtensionV2.step1(userId);
            executorService.execute(() -> {
                try {
                    ysAuditServiceExtensionV2.step2(userId);
                    ysAuditServiceExtensionV2.step3(userId);
                } catch (Exception e) {
                    e.printStackTrace();
                    final JftMercPaymentChannel entity = mpcService.getByUserId(userId);
                    if (entity == null) {
                        return;
                    }

                    entity.setStatus(MerchantChannelStatus.AUDIT_STATUS_REJECT);
                    entity.setRemark("缴费通功能开通失败: " + e.getMessage());
                    entity.setUpdateTime(new Date());
                    mpcService.updateById(entity);
                }
            });
        } catch (Exception e) {
            final JftMercPaymentChannel entity = mpcService.getByUserId(userId);
            if (entity != null) {
                entity.setRemark("缴费通功能开通失败: " + e.getMessage());
                entity.setStatus(MerchantChannelStatus.AUDIT_STATUS_REJECT);
                entity.setUpdateTime(new Date());

                // 这里将驳回的信息放入新的事务中，防止被回滚
                transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                    @Override
                    protected void doInTransactionWithoutResult(@NotNull TransactionStatus status) {
                        mpcService.updateById(entity);
                    }
                });

            }

            throw e;
        }

    }

    public void preCheck(String userId) {
        LambdaQueryWrapper<JftMercPaymentChannel> qWrapper2 = Wrappers.lambdaQuery();
        qWrapper2.eq(JftMercPaymentChannel::getUserId, userId);

        final JftMercPaymentChannel one = mpcService.getOne(qWrapper2);
        if (one != null) {
            switch (one.getStatus()) {
                case MerchantChannelStatus.AUDIT_STATUS_SUCCESS:
                    throw new MsgException("功能已开通，请勿重复操作");
                case MerchantChannelStatus.AUDIT_STATUS_EXAMINING:
                    throw new MsgException("功能开通审核中，请耐心等待审核结果");
                case MerchantChannelStatus.AUDIT_STATUS_REJECT:
                    mpcService.removeById(one.getId());
                default:
                    break;
            }
        }

        final IdCard certIdCard = idCardService.getCertIdCard(userId);
        if (certIdCard == null) {
            throw new MsgException("实名身份证不能空");
        }

        UserInfo ui = uiService.getById(userId);
        if (StringUtils.isEmpty(ui.getChannelCode())) {
            // 开放接口注册的用户不受限制
            final Integer count = jpcMapper.selectCountByCertNo(certIdCard.getCertNo());

            if (count > 1) {
                throw new MsgException("同一实名信息只能开通一次该功能");
            }
        }
    }

    /**
     * 提交基本信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void step1(String userId) {
        JftMercBaseInfo jbi = jftAuditService.getMercBaseInfo(Long.parseLong(userId));
        JftMercPaymentChannel mpc = mpcService.getByUserId(userId);

        UserInfo ui = uiService.getById(userId);

        // 先保存一个进件中的记录
        if (mpc == null) {
            mpc = new JftMercPaymentChannel();
            mpc.setUserId(Long.parseLong(userId));
            mpc.setStatus(MerchantChannelStatus.AUDIT_STATUS_EXAMINING);
            mpcService.save(mpc);
        }

        JftBankCard bankCard = jbcService.getByUserId(Long.parseLong(userId));
        if (bankCard == null) {
            throw new MsgException("请先保存银行卡信息");
        }

        IdCard certIdCard = idCardService.getCertIdCard(userId);
        JSONObject param = new JSONObject();
        param.put(YsConstant.ALIAS, jbi.getAlias());
        param.put(YsConstant.BUSINESS_ADDRESS, jbi.getAddress());

        RegionCodeYs ysRegionCode = regionReflectService.getYsRegionCode(jbi.getProvince(), jbi.getCity(), jbi.getDistrict());
        param.put(YsConstant.MERC_PROV, ysRegionCode.getProvince());
        param.put(YsConstant.MERC_CITY, ysRegionCode.getCity());
        param.put(YsConstant.MERC_AREA, StringUtils.isEmpty(ysRegionCode.getArea()) ? ysRegionCode.getCity() : ysRegionCode.getArea());
        param.put(YsConstant.LEGAL_PERSON_NAME, certIdCard.getCertName());
        param.put(YsConstant.LEGAL_PERSON_ID, certIdCard.getCertNo());
        param.put(YsConstant.LEGAL_PERSON_START, YSConstants.getIdCardLongExpire(certIdCard.getCertStartTime()));
        param.put(YsConstant.LEGAL_PERSON_LIMIT, YSConstants.getIdCardLongExpire(certIdCard.getCertEndTime()));
        param.put(YsConstant.LEGAL_PERSON_PHONE, ui.getPhone().substring(0, 11));
        param.put(YsConstant.ACCOUNT_PERSON_NAME, certIdCard.getCertName());
        param.put(YsConstant.ACCOUNT_PERSON_ID, certIdCard.getCertNo());
        param.put(YsConstant.ACCOUNT_CARD_NO, bankCard.getBankCardNo());
        param.put(YsConstant.ACCOUNT_CARD_NAME, bankCard.getBankHolder());
        RegionCodeYs bankRegion = regionReflectService.getYsRegionCode(bankCard.getBranchProvince(), bankCard.getBranchCity(), bankCard.getBranchArea());
        param.put(YsConstant.BANK_PROVINCE, bankRegion.getProvince());
        param.put(YsConstant.BANK_CITY, bankRegion.getCity());
        param.put(YsConstant.BANK_AREA, StringUtils.isEmpty(bankRegion.getArea()) ? bankRegion.getCity() : bankRegion.getArea());
        BankCodeYs bankCodeYs = bankCodeYsMapper.selectByContactLine(bankCard.getContactLine());
        MsgException.checkNull(bankCodeYs, "银盛通道未找到对应的支行信息, 请重新选择支行");
        param.put(YsConstant.BANK_NAME, bankCodeYs.getBankName());
        param.put(YsConstant.BANK_TYPE, bankCodeYs.getBankType());
        param.put(YsConstant.BANK_BRANCH_TYPE, bankCodeYs.getContactLine());
        param.put(YsConstant.BANK_BRANCH_NAME, bankCodeYs.getBranchName());
        param.put(YsConstant.CONTACTS_NAME, certIdCard.getCertName());

        param.put(YsConstant.CONTACTS_PHONE, bankCard.getPhone());
        param.put(YsConstant.CONTACTS_EMAIL, "18062761507@163.com");

        JftMerchantRate jmrExist = jftMerchantRateService.getRateByUserId(userId);
        JftMerchantRate rate;
        if (jmrExist == null) {
            final JftMerchantRate auditRate = jftMerchantRateService.getAuditRate(Long.parseLong(userId));
            rate = new JftMerchantRate();
            rate.setChannel(4);
            rate.setAliRate(auditRate.getAliRate());
            rate.setUserId(Long.parseLong(userId));
            jftMerchantRateService.save(rate);
        } else {
            rate = jmrExist;
        }

        param.put(YsConstant.WECHAT_SIGN_RATE, (rate.getAliRate().intValue() / 100f) + "");
        param.put(YsConstant.WECHAT_SIGN_MIN, "10");
        param.put(YsConstant.ALIPAY_SIGN_RATE, (rate.getAliRate().intValue() / 100f) + "");
        param.put(YsConstant.ALIPAY_SIGN_MIN, "10");
        param.put(YsConstant.CREDIT_RATE_1, "0.61");
        param.put(YsConstant.CREDIT_MIN_1, "10");
        param.put(YsConstant.DEBIT_RATE_1, "0.61");
        param.put(YsConstant.DEBIT_MIN_1, "10");
        param.put(YsConstant.DEBIT_MAX_1, "999900");
        param.put(YsConstant.CREDIT_RATE_2, "0.53");
        param.put(YsConstant.CREDIT_MIN_2, "10");
        param.put(YsConstant.DEBIT_RATE_2, "0.53");
        param.put(YsConstant.DEBIT_MIN_2, "10");
        param.put(YsConstant.DEBIT_MAX_2, "999900");
        param.put(YsConstant.OPEN_POS_P, "0");
        param.put(YsConstant.FAST_PAY, "0");
        param.put(YsConstant.CODE_ADVANCE_OPEN, "1");
        param.put(YsConstant.CODE_LEGAL_PERSON_ACC, "1");
        param.put(YsConstant.CODE_ADVANCE_RATE, "0.10");
        // 最低垫资费1角
        param.put(YsConstant.CODE_ADVANCE_MIN, "1");
        MccReflect mccReflect = mccReflectService.getYsMccReflect(jbi.getMcc());
        MccInfoYs mccInfoYs = new MccInfoYs();
        mccInfoYs.setMccCode(mccReflect.getMccCode());
        mccInfoYs = mccInfoYsService.getOne(new QueryWrapper<>(mccInfoYs));
        MsgException.checkNull(mccInfoYs, "对应的MCC缺少银盛商户类型");
        MsgException.checkBlank(mccInfoYs.getType(), "对应的MCC缺少银盛商户类型");

        if ("N".equals(mccInfoYs.getMicro()) && jbi.getMerchantType().equals(MerchantBaseInfo.MERCH_TYPE_MICRO)) {
            throw new MsgException("小微不支持该mcc");
        }

        if ("N".equals(mccInfoYs.getIndividual()) && jbi.getMerchantType().equals(MerchantBaseInfo.MERCH_TYPE_INDIVIDUAL)) {
            throw new MsgException("个体户不支持该mcc");
        }

        if ("N".equals(mccInfoYs.getCompan()) && jbi.getMerchantType().equals(MerchantBaseInfo.MERCH_TYPE_COMPANY)) {
            throw new MsgException("企业不支持该mcc");
        }

        param.put(YsConstant.MCC_SUB_CD, mccInfoYs.getType());
        param.put(YsConstant.MCC_CD, mccInfoYs.getMccCode());
        param.put(YsConstant.INDUSTRY, mccInfoYs.getIndustry());
        param.put(YsConstant.MCH_TYPE, mccInfoYs.getMchType());
        param.put(YsConstant.CRP_GENDER, StringUtil.getSexFromCertNo(certIdCard.getCertNo()));
        MccInfoSort mccInfoSort = mccInfoSortService.getByMcc(jbi.getMcc());
        param.put(YsConstant.CRP_PROFESSION, mccInfoSort.getCode());
        param.put(YsConstant.CRP_ADDR, jbi.getAddress());
        param.put(YsConstant.USER_TYPE, AuditUtils.getYsMerchantType(jbi.getMerchantType()));
        param.put(YsConstant.LOWEST_STL_AMT, "1");
        param.put(YsConstant.BANK_MOBILE, bankCard.getPhone());
        param.put(YsConstant.ACTUAL_MANAGER, certIdCard.getCertName());
        param.put(YsConstant.CERT_TYPE, "00");
        param.put(YsConstant.MERC_LVL, "1");
        param.put(YsConstant.CONTACTS_ADDR, jbi.getAddress());
        param.put(YsConstant.BALANCE_ACC_TYPE, "11");
        param.put(YsConstant.EXPANDING_MANAGER, "收银呗");

        JSONObject data = ysServiceV2.req(YsConfigExtensionV2.MER_AUDIT, ReqMethod.MERCHANT_ADD, param);
        RespEntity respEntity = JSON.toJavaObject(data, RespEntity.class);
        if (!"00".equals(respEntity.getCode())) {
            throw new MsgException(respEntity.getNote());
        }

        mpc.setApplicationId(respEntity.getSysFlowId());
        mpc.setThirdStatus(MerchantChannelStatus.AUDIT_THIRD_STATUS_AUDITING);
        mpcService.updateById(mpc);
    }

    /**
     * 上传图片
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void step2(String userId) {
        JftMercPaymentChannel mpc = mpcService.getByUserId(userId);

        List<MerchantImage> miList = jftAuditService.listMerchImage(Long.parseLong(userId));

        for (MerchantImage image : miList) {
            image.setPhotoUrl(mpc.getApplicationId());
            if (StringUtils.isEmpty(image.getPicUrl())) {
                continue;
            }

            if (MerchantImage.ImageType.IDCARD_FRONT.equals(image.getPhotoType())) {
                // 小微没有法人身份证正面
                continue;
            }

            if (MerchantImage.ImageType.IDCARD_BACK.equals(image.getPhotoType())) {
                // 小微没有法人身份证反面
                continue;
            }

            // 小微法人信息用结算身份证信息
            reusedPic(image, MerchantImage.ImageType.SETTLE_IDCARD_FRONT, MerchantImage.ImageType.IDCARD_FRONT);
            reusedPic(image, MerchantImage.ImageType.SETTLE_IDCARD_BACK, MerchantImage.ImageType.IDCARD_BACK);
            reusedPic(image, MerchantImage.ImageType.SETTLE_IDCARD_FRONT, MerchantImage.ImageType.HAND_IDCARD_PHOTO);

            // 银行卡反面图
            reusedPic(image, MerchantImage.ImageType.BANK_CARD_FRONT, MerchantImage.ImageType.BANK_CARD_BACK);

            imageUpload(image);
        }
    }

    /**
     * 符合条件的图片类型以新的图片类型再次使用
     *
     * @param src     商户图片数据
     * @param reqType 需要的图片原始类型
     * @param newType 新的图片类型
     */
    private boolean reusedPic(MerchantImage src, String reqType, String newType) {
        if (src.getPhotoType().equals(reqType)) {
            String originType = src.getPhotoType();
            src.setPhotoType(newType);
            imageUpload(src);
            src.setPhotoType(originType);

            return true;
        }

        return false;
    }

    /**
     * 发起进件
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void step3(String userId) {
        // 一码多户开通不需要这一步操作
        JftMercPaymentChannel jpc = jpcService.getByUserId(userId);

        JftMercBaseInfo jmbi = jmbiService.getMercBaseInfo(Long.parseLong(userId));

        JSONObject data = new JSONObject();
        data.put("sysFlowId", jpc.getApplicationId());
        JSONObject resp = ysServiceV2.req(YsConfigV2.MER_APPLY, ReqMethod.MERCHANT_APPLY, data);

        RespEntity respEntity = JSON.toJavaObject(resp, RespEntity.class);
        jpc.setThirdStatus(cn.pluss.platform.ys.impl.v20210929.YsConstant.AuditStatus.AUDIT_NO_AUDIT.getCode());

        String url = respEntity.getSignUrl();
        if (StringUtils.isNotEmpty(url)) {
            // 有签约链接即代表初审通过
            jpc.setThirdStatus(cn.pluss.platform.ys.impl.v20210929.YsConstant.AuditStatus.AUDIT_FIRST_TRIAL_SUCCESS.getCode());
            jpc.setStatus(MerchantChannelStatus.AUDIT_STATUS_WAITING_SIGN);
            Notice notice = new Notice(1, -1, jpc.getUserId() + "");
            String uniqueKey = "YS_QY" + System.currentTimeMillis();
            String title = "电子协议签约通知";
            String content = "尊敬的客户您好，请点此通知完成签约意愿授权。请注意，同意授权后，支付通道才会进行商户资料审核，否则会一直等待审核。";

            notice.setUniqueKey(uniqueKey);
            notice.setNoticeCode(uniqueKey);
            notice.setTitle(title);
            notice.setUrl(url);
            notice.setDesc(content);
            noticeService.save(notice);

            generalPushUtil.sendAllPlatByAlias(Collections.singletonList(jpc.getUserId() + ""), title, content, "1");

        }

        jpc.setExtra(resp);
        jpc.setUpdateTime(new Date());
        jpcService.updateById(jpc);
    }

    @Override
    public void merchantAuditCheck(String userId, boolean isFailCheck) {
        // nothing to do
    }

    @Override
    public void editMerchantAudit(String userId) {
        // 进件参数拼接
        JSONObject paramMap = new JSONObject();
        JftMercPaymentChannel jmpc = jpcService.getByUserId(userId);

        paramMap.put(RyxConstants.MER_IN_ID, jmpc.getExtra().getString(RyxConstants.MER_IN_ID));

        LambdaQueryWrapper<JftBankCard> qWrapper = Wrappers.lambdaQuery();
        qWrapper.eq(JftBankCard::getUserId, userId);
        JftBankCard jftBankCard = jbcService.getByUserId(Long.parseLong(userId));
        JSONObject bak = jftBankCard.getBak();

        BankCard newBankCard = JSON.toJavaObject(bak, BankCard.class);
        BankCodeYs bankCodeYs = bankCodeYsMapper.selectByContactLine(newBankCard.getContactLine());
        RegionCodeYs bankRegion = regionReflectService.getYsRegionCode(newBankCard);
        paramMap.put(YsConstant.MERC_ID, jmpc.getMerchantId());
        paramMap.put(YsConstant.ACCOUNT_CARD_NAME, newBankCard.getBankHolder());
        paramMap.put(YsConstant.ACCOUNT_CARD_NO, newBankCard.getBankCardNo());
        paramMap.put(YsConstant.BANK_PROVINCE, bankRegion.getProvince());
        paramMap.put(YsConstant.BANK_CITY, bankRegion.getCity());
        paramMap.put(YsConstant.BANK_NAME, bankCodeYs.getBankName());
        paramMap.put(YsConstant.BANK_TYPE, bankCodeYs.getBankType());
        paramMap.put(YsConstant.BANK_BRANCH_TYPE, bankCodeYs.getContactLine());
        paramMap.put(YsConstant.BANK_BRANCH_NAME, bankCodeYs.getBankName());
        paramMap.put(YsConstant.BANK_MOBILE, newBankCard.getPhone());

        /*
         * {
         * 	"charset":"UTF-8",
         * 	"data":"{\"note\":\"商户账户信息变更提交成功\",\"code\":\"00\",\"changeFlowId\":\"20211210164429426221368030546\"}",
         * 	"method":"changeMerAccInfo",
         * 	"msgCode":"Merchant",
         * 	"reqFlowId":"YS5869563172",
         * 	"signType":"RSA",
         * 	"signValue":"mKw+LR01rDNnkinQ0twx6DBEJYiKOezqh0gy4OAaTpRpVea4G/2Ck23wDFlXXz3/UC0wYPmBOS2AhubpY4L4t8y3aTPS5HE7ZpB8eg3NxPg+1MoGU/9/PqI6SFgqdTYQfUXEZFbDZwBKVhZEc4bUXy76Md/V53SBeMLjW0FCElo=",
         * 	"src":"ORG20211020190222",
         * 	"ver":"1.0"
         * }
         */
        JSONObject req = ysServiceV2.req(YsConfigExtensionV2.MER_BANK_CHANGE, ReqMethod.MERCHANT_ACCOUNT_CHANGE, paramMap);

        String changeFlowId = req.getString("changeFlowId");
        jmpc.getExtra().put("changeFlowId", changeFlowId);
        jmpc.setStatus(MerchantChannelStatus.AUDIT_STATUS_DATA_EDIT);
        jpcService.updateById(jmpc);

        // 上传银行卡正面图片
        imageUploadEdit(newBankCard.getImgUrl(), "A004", changeFlowId);
        // 上传银行卡反面图片
        imageUploadEdit(newBankCard.getImgUrl(), "A005", changeFlowId);
        // 上传一张空白图作为结算信息修改申请表
        imageUploadEdit("https://www.shouyinbei.net/resources/images/upload/blank.png", "B008", changeFlowId);
    }

    /**
     * 修改商户信息，上传图片
     */
    public void imageUploadEdit(String url, String picType, String changeFlowId) {
        String imgBase64Data = FileUtil.getBase64FromUrl(url);

        try {
            if (!Objects.equals(url, MerchantImage.ImageType.BUSINESS_LICENSE)) {
                log.debug("压缩图片");
            } else {
                log.debug("压缩图片 - 营业执照");
            }
            imgBase64Data = Base64Util.compressPic(imgBase64Data, 1024);
        } catch (Exception e) {
            LogExceptionUtils.printStack(log, e, null);
            throw new MsgException("上传文件失败");
        }

        JSONObject param = new JSONObject();
        param.put("changeFlowId", changeFlowId);
        param.put("picType", picType);
        try {
            byte[] bytes = Base64.getDecoder().decode(imgBase64Data);
            ByteArrayResource byteArrayResource = new ByteArrayResource(bytes) {
                @Override
                public String getFilename() {
                    return picType + ".png";
                }
            };
            String encryptOriginData = SignUtils.getSignContent(param);
            log.info("加签源数据==============>" + encryptOriginData);
            param.put("signValue", RSAUtils.sign(ysConfig.getPriKeyV2(), encryptOriginData));
            param.put("picFile", byteArrayResource);
        } catch (Exception e) {
            log.error("数据异常" + e.getMessage());
        }


        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        int retryTimes = 3;
        while (true) {
            param.put("reqFlowId", "YS" + System.currentTimeMillis() % 10000000 + UUID.randomUUID().toString().substring(4, 7));

            headers.add("User-Agent",
                    "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36");

            MultiValueMap<String, Object> reqParam = MapUtils.map2MultiValueMap(param);
            HttpEntity<MultiValueMap<String, Object>> reqEntity = new HttpEntity<>(reqParam, headers);

            try {
                String respStr = restTemplate.postForObject(YsConfigExtensionV2.MER_EDIT_PIC_UPLOAD, reqEntity, String.class);
                log.info("result : " + respStr);
                JSONObject resultJson = JSON.parseObject(respStr);
                JSONObject data = resultJson.getJSONObject("data");
                if (!"00".equals(data.getString("code"))) {
                    throw new MsgException(data.getString("note"));
                }
                break;
            } catch (Exception e) {
                log.info(e.getClass().getName());
                // 超时后自动重试
                retryTimes--;
                if (retryTimes <= 0) {
                    throw new MsgException("图片3次上传失败：" + e.getMessage());
                }
            }
        }
    }

    @Override
    public void checkSign(JSONObject receiveData) throws Exception {
        // nothing to do
    }

    @Override
    public JSONObject reqMerProtocolStatus(JftMercPaymentChannel jpc) {
        String sysSignFlowId = jpc.getExtra().getString("sysSignFlowId");
        if (sysSignFlowId == null) {
            // 缺少签约参数, 发起签约申请
            sendProtocolMsg(jpc);
            return new JSONObject();
        }

        JSONObject data = new JSONObject();
        data.put("sysSignFlowId", sysSignFlowId);
        JSONObject result = ysServiceV2.req(YsConfigExtensionV2.MER_PROTOCOL_STATUS, ReqMethod.MERCHANT_PROTOCOL_STATUS, data);

        if ("2".equals(result.getString("signSts"))) {
            // 已签约
            jpc.setStatus(MerchantChannelStatus.AUDIT_STATUS_SIGNED);
            jpcService.updateById(jpc);
            return new JSONObject();
        }

        if ("1".equals(result.getString("signSts")) || "8".equals(result.getString("signSts"))) {
            // 待签署, 则检查签约是否超时, 这里的超时判断就用进件记录的更新时间
            // long maxLimitTime = 3600 * 1000 * 2;
            // // 签约短信2小时失效
            // boolean expireFlag = (System.currentTimeMillis() - mcs.getUpdateTime().getTime()) > maxLimitTime;
            //
            // if (expireFlag) {
            //     // 短信已经过期, 则重新发起签约申请
            //     sendProtocolMsg(mcs);
            //     return new JSONObject();
            // }
        }

        return new JSONObject();
    }

    @Override
    public JSONObject sendProtocolMsg(JftMercPaymentChannel jpc) {
        JSONObject data = new JSONObject();
        data.put("sysFlowId", jpc.getApplicationId());
        data.put("isSendMsg", "N");
        JSONObject respResult = ysServiceV2.req(YsConfigExtensionV2.MER_PROTOCOL_MSG, ReqMethod.MERCHANT_SEND_MSG, data);

        if ("00".equals(respResult.getString("code"))) {
            jpc.getExtra().put("sysSignFlowId", respResult.getString("sysSignFlowId"));
            jpc.getExtra().put("signUrl", respResult.getString("signUrl"));

            Notice notice = new Notice(1, -1, jpc.getUserId() + "");
            String uniqueKey = "YS_QY" + System.currentTimeMillis();
            String title = "电子协议签约通知";
            String content = "尊敬的客户您好，请点此通知完成签约意愿授权。请注意，同意授权后，支付通道才会进行商户资料审核，否则会一直等待审核。";
            String url = respResult.getString("signUrl");
            notice.setUniqueKey(uniqueKey);
            notice.setNoticeCode(uniqueKey);
            notice.setTitle(title);
            notice.setUrl(url);
            notice.setDesc(content);
            noticeService.save(notice);

            generalPushUtil.sendAllPlatByAlias(Collections.singletonList(jpc.getUserId() + ""), title, content, "1");

            jpc.setStatus(MerchantChannelStatus.AUDIT_STATUS_WAITING_SIGN);
            jpcService.updateById(jpc);
        }

        return new JSONObject();
    }

    /**
     * 开通线上支付
     */
    @Override
    public void openOnlinePay(JftMercPaymentChannel mcs) {
        UserApp ua = uaService.getUserAppByUserId(mcs.getUserId());

        if (StringUtils.isEmpty(mcs.getMerchantId())) {
            throw new MsgException("开通线上支付失败, 缺少商编");
        }

        int attemptCount = 5;

        Exception throwException = null;

        JftMerchantRate auditRate = jftMerchantRateService.getRateByUserId(mcs.getUserId());
        auditRate.setMerchantId(mcs.getMerchantId());
        jftMerchantRateService.updateById(auditRate);

        final int rateVal = auditRate.getAliRate().intValue();

        while (attemptCount > 0) {
            try {
                JSONObject param = new JSONObject();
                param.put(YsConstant.MERC_ID, mcs.getMerchantId());
                param.put(YsConstant.ACC_ORG_NO, ysConfig.getSrc());
                param.put(YsConstant.SCAN_SERVICE_OPEN, "1,2,3,4,5,6");
                JSONObject rate = new JSONObject();
                param.put(YsConstant.EASY_FEE_BEAN, rate);

                // 支付宝
                rate.put(YsConstant.R_SCAN_WX_FEE_TYPE, "1");
                rate.put(YsConstant.R_SCAN_WX_FEE_RATE, (rateVal / 100f) + "");
                rate.put(YsConstant.R_SCAN_WX_FEE_NUM, "1");
                // 微信
                rate.put(YsConstant.R_SCAN_ALI_FEE_TYPE, "1");
                rate.put(YsConstant.R_SCAN_ALI_FEE_RATE, (rateVal / 100f) + "");
                rate.put(YsConstant.R_SCAN_ALI_FEE_NUM, "1");
                // 银联借记卡 > 1000
                rate.put(YsConstant.R_SCAN_DEBIT_TYPE_1, "1");
                rate.put(YsConstant.R_SCAN_DEBIT_NUM_1, "0.61");
                rate.put(YsConstant.R_SCAN_DEBIT_MIN_1, "1");
                rate.put(YsConstant.R_SCAN_DEBIT_MAX_1, "999900");

                // 银联借记卡 ≤ 1000
                rate.put(YsConstant.R_SCAN_DEBIT_TYPE_2, "1");
                rate.put(YsConstant.R_SCAN_DEBIT_NUM_2, (rateVal / 100f) + "");
                rate.put(YsConstant.R_SCAN_DEBIT_MIN_2, "1");
                rate.put(YsConstant.R_SCAN_DEBIT_MAX_2, "999900");

                // 银联贷记卡 > 1000
                rate.put(YsConstant.R_SCAN_CREDIT_TYPE_1, "1");
                rate.put(YsConstant.R_SCAN_CREDIT_NUM_1, "0.61");
                rate.put(YsConstant.R_SCAN_CREDIT_MIN_1, "1");

                // 银联贷记卡 ≤ 1000
                rate.put(YsConstant.R_SCAN_CREDIT_TYPE_2, "1");
                rate.put(YsConstant.R_SCAN_CREDIT_NUM_2, (rateVal / 100f) + "");
                rate.put(YsConstant.R_SCAN_CREDIT_MIN_2, "1");

                try {
                    // 开通线上支付T1
                    ysServiceV2.req(YsConfigExtensionV2.MER_T1_REQ, ReqMethod.MERCHANT_ONLINE_TRADING, param);
                } catch (Exception e) {
                    e.printStackTrace();
                }

                param = new JSONObject();
                param.put(YsConstant.MERC_ID, mcs.getMerchantId());
                param.put("opType", "1");

                if ("LL".equals(ua.getUserType())) {
                    param.put("feeRate", "0.04");
                } else {
                    param.put("feeRate", "0.10");
                }

                param.put("feeMin", "1");

                ysServiceV2.req(YsConfigExtensionV2.MER_D0_REQ, ReqMethod.MERCHANT_OPT_ONLINE_D0, param);
                return;
            } catch (MsgException e) {
                throwException = e;
                break;
            } catch (Exception e) {
                throwException = e;
                attemptCount--;
            }
        }

        String message = throwException.getMessage();
        log.error("开通线上支付失败: {}", message);
    }
}
