package cn.pluss.platform.ys.impl.v20210929;

import cn.pluss.platform.IdCardService;
import cn.pluss.platform.dto.AccountDTO;
import cn.pluss.platform.entity.*;
import cn.pluss.platform.exception.MsgException;
import cn.pluss.platform.mapper.AccountMapper;
import cn.pluss.platform.mapper.BankCodeYsMapper;
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.MerchantBaseInfoService;
import cn.pluss.platform.merchantImage.MerchantImageService;
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.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 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("ysAuditServiceV2")
public class YsAuditServiceImpl extends AbstractYsResultService {

    @Autowired
    private GeneralPushUtil generalPushUtil;

    @Autowired
    private MerchantBaseInfoService mbiService;

    @Autowired
    private AccountService accountService;

    @Resource
    private AccountMapper accountMapper;

    @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 MerchantImageService miService;

    @Autowired
    private YsServiceV2 ysServiceV2;

    @Autowired
    private YsConfigV2 ysConfig;

    @Autowired
    private NoticeService noticeService;

    @Autowired
    private UserInfoService uiService;

    @Autowired
    private YsAuditServiceImpl ysAuditServiceV2;

    @Autowired
    private ExecutorService executorService;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void merchantAudit(String userId, boolean isFailCheck) {

        preCheck(userId);
        try {
            ysAuditServiceV2.step1(userId);
            executorService.execute(() -> {
                try {
                    ysAuditServiceV2.step2(userId);
                    ysAuditServiceV2.step3(userId);
                } catch (Exception e) {
                    e.printStackTrace();
                    MerchantBaseInfo mbi = mbiService.getMerchantBaseInfoByUserId(userId);
                    MerchantChannelStatus mcs = mcsService.getByMerchantCode(mbi.getMerchantCode(), 4);

                    if (mcs != null && mcs.getChannel() == 4) {
                        mcs.setStatus(MerchantChannelStatus.AUDIT_STATUS_EXAMINING);
                        mcs.setThirdStatus(MerchantChannelStatus.AUDIT_THIRD_STATUS_WAITING);
                        mcs.setRemark("银盛审核失败: " + e.getMessage());
                        mcs.setUpdateTime(new Date());
                        mcsService.updateById(mcs);
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            MerchantBaseInfo mbi = mbiService.getMerchantBaseInfoByUserId(userId);
            MerchantChannelStatus mcs = mcsService.getByMerchantCode(mbi.getMerchantCode(), 4);

            if (mcs != null && Objects.equals(mcs.getChannel(), 4)) {
                mcs.setStatus(MerchantChannelStatus.AUDIT_STATUS_EXAMINING);
                mcs.setThirdStatus(MerchantChannelStatus.AUDIT_THIRD_STATUS_WAITING);
                mcs.setRemark("银盛审核失败: " + e.getMessage());
                mcs.setUpdateTime(new Date());

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

            throw e;
        }

    }

    public void preCheck(String userId) {
        MerchantBaseInfo mbi = mbiService.getMerchantBaseInfoByUserId(userId);
        LambdaQueryWrapper<MerchantChannelStatus> qWrapper = Wrappers.lambdaQuery();
        qWrapper.eq(MerchantChannelStatus::getMerchantCode, mbi.getMerchantCode());
        qWrapper.eq(MerchantChannelStatus::getVirChannelFlag, Account.CHANNEL_TYPE_D1);
        MerchantChannelStatus existD1 = mcsService.getOne(qWrapper);

        if (existD1 != null && !MerchantChannelStatus.AUDIT_STATUS_SUCCESS.equals(existD1.getStatus())
                && !MerchantChannelStatus.AUDIT_STATUS_SIGNED.equals(existD1.getStatus())
                && !MerchantChannelStatus.AUDIT_STATUS_WAITING_SIGN.equals(existD1.getStatus())) {
            throw new MsgException("当前D1通道存在未进件完成的的进件数据，不允许进件银盛");
        }

        if ("1".equals(mbi.getMerchantType())) {
            int ysCount = accountMapper.selectYSAuditSmallCount(mbi.getUserId());
            if (ysCount >= 2) {
                MsgException.throwException("非营业执照商户同一结算人仅可申请两次实时到账");
            }
        }

        qWrapper.clear();
        qWrapper.eq(MerchantChannelStatus::getMerchantCode, mbi.getMerchantCode());
        qWrapper.eq(MerchantChannelStatus::getVirChannelFlag, mbi.getMerchantCode());
        MerchantChannelStatus existD0 = mcsService.getOne(qWrapper);

        if (existD0 != null) {
            existD0 = mcsService.getByIdLock(existD0.getId());

            if (existD0.getStatus().equals(MerchantChannelStatus.AUDIT_STATUS_REJECT)) {
                return;
            }

            if (existD0.getStatus().equals(MerchantChannelStatus.AUDIT_STATUS_EXAMINING)
                && existD0.getThirdStatus().equals(MerchantChannelStatus.AUDIT_THIRD_STATUS_AUDITING)) {
                throw new MsgException("当前商户已经在后台审核中");
            }
        }
    }

    /**
     * 提交基本信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void step1(String userId) {
        MerchantBaseInfo mbi = mbiService.getMerchantBaseInfoByUserId(userId);
        MerchantChannelStatus mcs = mcsService.getByMerchantCode(mbi.getMerchantCode(), 4);
        MsgException.checkNull(mcs, "商户暂未请求进件");
        mcs.setChannel(4);

        AccountDTO account = accountService.getRealAccount(userId, Account.CHANNEL_TYPE_D0);
        IdCard legalIdCard = idCardService.getLegalIdCard(userId);
        if (legalIdCard == null) {
            legalIdCard = account.getIdcard();
        }
        JSONObject param = new JSONObject();
        if (!"1".equals(mbi.getMerchantType())) {
            param.put(YsConstant.BUSINESS_LICENCE, mbi.getBussAuthName());
            param.put(YsConstant.BUSINESS_LICENCE_NO, mbi.getBussAuthNum());
            param.put(YsConstant.BUSINESS_LICENCE_START, mbi.getBussAuthStartTime().replace("-", ""));
            param.put(YsConstant.BUSINESS_LICENCE_LIMIT, YSConstants.getMerLicenseEnd(mbi.getBussAuthEndTime()));
            param.put(YsConstant.REGISTERED_ADDRESS, mbi.getBussAuthAddress());
        }
        param.put(YsConstant.ALIAS, mbi.getAlias());
        param.put(YsConstant.BUSINESS_ADDRESS, mbi.getAddress());

        RegionCodeYs ysRegionCode = regionReflectService.getYsRegionCode(mbi.getProvince(), mbi.getCity(), mbi.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, legalIdCard.getCertName());
        param.put(YsConstant.LEGAL_PERSON_ID, legalIdCard.getCertNo());
        param.put(YsConstant.LEGAL_PERSON_START, YSConstants.getIdCardLongExpire(legalIdCard.getCertStartTime()));
        param.put(YsConstant.LEGAL_PERSON_LIMIT, YSConstants.getIdCardLongExpire(legalIdCard.getCertEndTime()));
        param.put(YsConstant.LEGAL_PERSON_PHONE, mbi.getContactMobile());
        param.put(YsConstant.ACCOUNT_PERSON_NAME, account.getIdcard().getCertName());
        param.put(YsConstant.ACCOUNT_PERSON_ID, account.getIdcard().getCertNo());
        param.put(YsConstant.ACCOUNT_CARD_NO, account.getBankCard().getBankCardNo());
        param.put(YsConstant.ACCOUNT_CARD_NAME, account.getBankCard().getBankHolder());
        RegionCodeYs bankRegion = regionReflectService.getYsRegionCode(account.getBankCard());
        param.put(YsConstant.BANK_PROVINCE, bankRegion.getProvince());
        param.put(YsConstant.BANK_CITY, bankRegion.getCity());

        if (!StringUtils.isEmpty(bankRegion.getArea())) {
            param.put(YsConstant.BANK_AREA, bankRegion.getArea());
        }

        BankCodeYs bankCodeYs = bankCodeYsMapper.selectByContactLine(account.getBankCard().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, account.getIdcard().getCertName());
        UserInfo ui = uiService.getById(userId);
        param.put(YsConstant.CONTACTS_PHONE, ui.getPhone().substring(0, 11));
        param.put(YsConstant.CONTACTS_EMAIL, "chaozhanggui2023@163.com");
        param.put(YsConstant.WECHAT_SIGN_RATE, "0.38");
        param.put(YsConstant.WECHAT_SIGN_MIN, "1");
        param.put(YsConstant.ALIPAY_SIGN_RATE, "0.38");
        param.put(YsConstant.ALIPAY_SIGN_MIN, "1");
        param.put(YsConstant.CREDIT_RATE_1, "0.61");
        param.put(YsConstant.CREDIT_MIN_1, "1");
        param.put(YsConstant.DEBIT_RATE_1, "0.61");
        param.put(YsConstant.DEBIT_MIN_1, "1");
        param.put(YsConstant.DEBIT_MAX_1, "999900");
        param.put(YsConstant.CREDIT_RATE_2, "0.40");
        param.put(YsConstant.CREDIT_MIN_2, "1");
        param.put(YsConstant.DEBIT_RATE_2, "0.40");
        param.put(YsConstant.DEBIT_MIN_2, "1");
        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");
        if (account.getSettleType().startsWith(Account.SETTLE_TYPE_PRIVATE_LEGAL)) {
            param.put(YsConstant.CODE_LEGAL_PERSON_ACC, "1");
        } else {
            param.put(YsConstant.CODE_LEGAL_PERSON_ACC, "0");
        }
        param.put(YsConstant.CODE_ADVANCE_RATE, "0.10");
        // 最低垫资费1角
        param.put(YsConstant.CODE_ADVANCE_MIN, "1");
        MccReflect mccReflect = mccReflectService.getYsMccReflect(mbi.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()) && mbi.getMerchantType().equals(MerchantBaseInfo.MERCH_TYPE_MICRO)) {
            throw new MsgException("小微不支持该mcc");
        }

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

        if ("N".equals(mccInfoYs.getCompan()) && mbi.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(legalIdCard.getCertNo()));
        MccInfoSort mccInfoSort = mccInfoSortService.getByMcc(mbi.getMcc());
        param.put(YsConstant.CRP_PROFESSION, mccInfoSort.getCode());
        param.put(YsConstant.CRP_ADDR, mbi.getAddress());
        param.put(YsConstant.USER_TYPE, AuditUtils.getYsMerchantType(mbi.getMerchantType()));
        param.put(YsConstant.LOWEST_STL_AMT, "1");
        param.put(YsConstant.BANK_MOBILE, account.getBankCard().getPhone());
        param.put(YsConstant.ACTUAL_MANAGER, mbi.getContactName());
        param.put(YsConstant.CERT_TYPE, "00");
        param.put(YsConstant.MERC_LVL, "1");
        param.put(YsConstant.CONTACTS_ADDR, mbi.getAddress());
        if (account.getSettleType().equals(Account.SETTLE_TYPE_CORPORATE)) {
            param.put(YsConstant.BALANCE_ACC_TYPE, "21");
        } else {
            param.put(YsConstant.BALANCE_ACC_TYPE, "11");
        }
        param.put(YsConstant.EXPANDING_MANAGER, "快银收银");

        // 这里在进件之前再加一次校验
        preCheck(userId);
        JSONObject data = ysServiceV2.req(YsConfigV2.MER_AUDIT, ReqMethod.MERCHANT_ADD, param);
        RespEntity respEntity = JSON.toJavaObject(data, RespEntity.class);
        if (!"00".equals(respEntity.getCode())) {
            throw new MsgException(respEntity.getNote());
        }

        mcs.setApplicationId(respEntity.getSysFlowId());
        mcs.setThirdStatus(MerchantChannelStatus.AUDIT_THIRD_STATUS_AUDITING);
        mcsService.updateById(mcs);
    }

    /**
     * 上传图片
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void step2(String userId) {
        AccountDTO account = accountService.getRealAccount(userId, Account.CHANNEL_TYPE_D0);

        MerchantBaseInfo mbi = mbiService.getMerchantBaseInfoByUserId(userId);
        MerchantChannelStatus mcs = mcsService.getByMerchantCode(mbi.getMerchantCode(), 4);

        // 进件上传图片
        MerchantImage merchantImage = new MerchantImage();
        merchantImage.setMerchantCode(mbi.getMerchantCode());
        List<MerchantImage> miList = miService.getListOfMerch(userId, Account.CHANNEL_TYPE_D0);

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

            if (!"1".equals(mbi.getMerchantType()) && image.getPhotoType().equals(MerchantImage.ImageType.HAND_IDCARD_PHOTO)) {
                // 非小微手持由营业执照图片代替
                continue;
            }

            if (!"1".equals(mbi.getMerchantType())
                    && Account.SETTLE_TYPE_CORPORATE.equals(account.getSettleType())
                    && image.getPhotoType().equals(MerchantImage.ImageType.BANK_CARD_FRONT)) {
                // 对公结算没有银行卡正面图
                continue;
            }

            if ("1".equals(mbi.getMerchantType())) {
                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);
            }

            imageUpload(image);

            if ("3".equals(mbi.getMerchantType()) && !Account.SETTLE_TYPE_CORPORATE.equals(account.getSettleType())) {
                // 企业非对公，用营业执照代替开户许可证
                reusedPic(image, MerchantImage.ImageType.BUSINESS_LICENSE, MerchantImage.ImageType.ACCOUNT_PERMITS);

                if (MerchantImage.ImageType.ACCOUNT_PERMITS.equals(image.getPhotoType())) {
                    continue;
                }
            }

            if (!"1".equals(mbi.getMerchantType())) {
                reusedPic(image, MerchantImage.ImageType.BUSINESS_LICENSE, MerchantImage.ImageType.HAND_IDCARD_PHOTO);

                if (account.getSettleType().startsWith(Account.SETTLE_TYPE_PRIVATE_ILLEGAL)) {
                    // 非法人对私，非法人结算函复用作手持
                    reusedPic(image, MerchantImage.ImageType.NON_LEG_SETTLE_AUTH, MerchantImage.ImageType.HAND_NON_LEG_SETTLE_AUTH);
                }
            }

            if (!"1".equals(mbi.getMerchantType()) && Account.SETTLE_TYPE_CORPORATE.equals(account.getSettleType())) {
                // 对公结算，开户许可证复用银行卡正面
                reusedPic(image, MerchantImage.ImageType.ACCOUNT_PERMITS, MerchantImage.ImageType.BANK_CARD_FRONT);
                // 对公结算，开户许可证复用银行卡反面
                reusedPic(image, MerchantImage.ImageType.ACCOUNT_PERMITS, MerchantImage.ImageType.BANK_CARD_BACK);
            } else {
                // 银行卡反面图
                reusedPic(image, MerchantImage.ImageType.BANK_CARD_FRONT, MerchantImage.ImageType.BANK_CARD_BACK);
            }
        }
    }

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

    /**
     * 发起进件
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void step3(String userId) {
        MerchantBaseInfo mbi = mbiService.getMerchantBaseInfoByUserId(userId);
        MerchantChannelStatus mcs = mcsService.getByMerchantCode(mbi.getMerchantCode(), 4);

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

        RespEntity respEntity = JSON.toJavaObject(resp, RespEntity.class);
        mcs.setChannel(4);
        mcs.setThirdStatus(YsConstant.AuditStatus.AUDIT_NO_AUDIT.getCode());

        String url = respEntity.getSignUrl();
        if (StringUtils.isNotEmpty(url)) {
            // 有签约链接即代表初审通过
            mcs.setThirdStatus(YsConstant.AuditStatus.AUDIT_FIRST_TRIAL_SUCCESS.getCode());
            mcs.setStatus(MerchantChannelStatus.AUDIT_STATUS_WAITING_SIGN);
            Notice notice = new Notice(1, -1, mbi.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(mbi.getUserId()), title, content, "1");
        }

        mcs.setExtra(resp);
        mcs.setUpdateTime(new Date());
        mcsService.updateById(mcs);
    }

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

    @Override
    public void editMerchantAudit(String userId) {
        MerchantBaseInfo mbi = mbiService.getMerchantBaseInfoByUserId(userId);
        // 进件参数拼接
        JSONObject paramMap = new JSONObject();
        MerchantChannelStatus mcs = mcsService.getByMerchantCode(mbi.getMerchantCode(), 4);

        if (4 != mcs.getChannel()) {
            throw new MsgException("修改通道选择错误");
        }
        paramMap.put(RyxConstants.MER_IN_ID, mcs.getExtra().getString(RyxConstants.MER_IN_ID));

        Account account = new Account().setUserId(userId).setChannelType(Account.CHANNEL_TYPE_D0);
        account = accountService.getOne(new QueryWrapper<>(account));
        JSONObject bak = account.getBak();

        BankCard newBankCard = JSON.toJavaObject(bak, BankCard.class);
        BankCodeYs bankCodeYs = bankCodeYsMapper.selectByContactLine(newBankCard.getContactLine());
        RegionCodeYs bankRegion = regionReflectService.getYsRegionCode(newBankCard);
        paramMap.put(YsConstant.MERC_ID, mcs.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(YsConfigV2.MER_BANK_CHANGE, ReqMethod.MERCHANT_ACCOUNT_CHANGE, paramMap);

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

        // 上传银行卡正面图片
        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(YsConfigV2.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(MerchantChannelStatus mcs) {
        String sysSignFlowId = mcs.getExtra().getString("sysSignFlowId");
        if (sysSignFlowId == null) {
            // 缺少签约参数, 发起签约申请
            sendProtocolMsg(mcs);
            return new JSONObject();
        }

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

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

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

        MerchantBaseInfo mbi = mbiService.getMerchantBaseInfoByMerchantCode(mcs.getMerchantCode());
        if ("00".equals(respResult.getString("code"))) {
            mcs.getExtra().put("sysSignFlowId", respResult.getString("sysSignFlowId"));
            mcs.getExtra().put("signUrl", respResult.getString("signUrl"));

            Notice notice = new Notice(1, -1, mbi.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(mbi.getUserId()), title, content, "1");

            mcs.setStatus(MerchantChannelStatus.AUDIT_STATUS_WAITING_SIGN);
            mcsService.updateById(mcs);
        }

        return new JSONObject();
    }

    /**
     * 开通线上支付
     */
    @Override
    public void openOnlinePay(MerchantChannelStatus mcs) {
        if (StringUtils.isEmpty(mcs.getMerchantId())) {
            throw new MsgException("开通线上支付失败, 缺少商编");
        }

        int attemptCount = 5;

        Exception throwException = null;

        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, "0.38");
                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, "0.38");
                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, "0.38");
                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, "0.38");
                rate.put(YsConstant.R_SCAN_CREDIT_MIN_2, "1");

                try {
                    // 开通线上支付T1
                    ysServiceV2.req(YsConfigV2.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");
                param.put("feeRate", "0.10");
                param.put("feeMin", "1");

                ysServiceV2.req(YsConfigV2.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);

        // MerchantBaseInfo mbi = mbiService.getMerchantBaseInfoByMerchantCode(mcs.getMerchantCode());
        // Notice notice = new Notice(1, -1, mbi.getUserId());
        // String uniqueKey = "YS_D0TZ" + System.currentTimeMillis();
        // String title = "系统通知";
        // String content = "实时到账开通失败,";
        //
        // if (message != null && !message.startsWith("银盛接口异常")) {
        //     content += message + ",";
        // }
        //
        // content += "详情可联系客服进行咨询";
        //
        // notice.setUniqueKey(uniqueKey);
        // notice.setNoticeCode(uniqueKey);
        // notice.setTitle(title);
        // notice.setDesc(content);
        // noticeService.save(notice);
        // GeneralPushUtil.sendAllPlatByAlias(Collections.singletonList(mbi.getUserId()), title, content, "1");
    }
}
