package com.crm.ucard.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.crm.rely.backend.configuration.LocaleMessage;
import com.crm.rely.backend.core.constant.*;
import com.crm.rely.backend.core.dto.base.BaseResultDto;
import com.crm.rely.backend.core.dto.base.PageDto;
import com.crm.rely.backend.core.dto.base.ResultWithPagerDto;
import com.crm.rely.backend.core.dto.custom.file.CustomFileDto;
import com.crm.rely.backend.core.dto.ib.account.AccountInfoDto;
import com.crm.rely.backend.core.dto.ucard.*;
import com.crm.rely.backend.core.dto.wasabi.*;
import com.crm.rely.backend.core.entity.base.BaseDeleteEntities;
import com.crm.rely.backend.core.entity.base.BaseEntity;
import com.crm.rely.backend.core.entity.base.SingleLongEntity;
import com.crm.rely.backend.core.entity.custom.info.CustomInfoEntity;
import com.crm.rely.backend.core.entity.custom.info.InfoEntity;
import com.crm.rely.backend.core.entity.custom.kyc.CustomKycVerifyRecodeEntity;
import com.crm.rely.backend.core.entity.custom.kyc.WebsdkLinkAliEntity;
import com.crm.rely.backend.core.entity.finance.balance.FinanceBalanceAddEntity;
import com.crm.rely.backend.core.entity.ib.account.AccountInfoListEntity;
import com.crm.rely.backend.core.entity.system.email.SysEmailSendEntity;
import com.crm.rely.backend.core.entity.ucard.*;
import com.crm.rely.backend.core.entity.wasabi.*;
import com.crm.rely.backend.core.entity.websocket.WebsocketPushMessageByCustomIdEntity;
import com.crm.rely.backend.core.enums.*;
import com.crm.rely.backend.core.pojo.table.*;
import com.crm.rely.backend.core.pojo.table.ucard.*;
import com.crm.rely.backend.exception.ServiceException;
import com.crm.rely.backend.service.EmailService;
import com.crm.rely.backend.service.MqSendService;
import com.crm.rely.backend.service.RedisService;
import com.crm.rely.backend.util.*;
import com.crm.ucard.config.WasabiConfig;
import com.crm.ucard.dao.mapper.*;
import com.crm.ucard.dao.repository.*;
import com.crm.ucard.model.ResultData;
import com.crm.ucard.model.TwoTuple;
import com.crm.ucard.service.*;
import com.crm.ucard.util.ObjectIdUtil;
import com.crm.ucard.util.RateLimiterUtil;
import com.crm.ucard.util.RsaUtils;
import com.crm.ucard.util.WasabiCommonUtil;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.core.env.Environment;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;

@Slf4j
@Service
public class WasabiCardServiceImpl implements WasabiCardService {

    public static final String MERCHANT_API_KEY = "f9ddaac1-bec6-42d8-8cc9-876d41b4ca1f-089a2225-d5e2-4733-af8c" +
            "-d8f45536a26f";
    public static final String MERCHANT_PRIVATE_KEY =
            "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAIiyenvvlFBKCgVqEt01D3A+8aEYE5tCm4O+pVP2Flci5Enp" +
                    "/rWiXpTeEf7nwKVbUDwc0DX9fnV81hWas6p4mAGvm5QUTBoZsF1wqXp" +
                    "/zc4Zd0sijCUEwJEWBJ9bToC30KBraUGBOasgfOXmEWk3BZ" +
                    "/jGK7q0JHhYPguZH9mZv2lAgMBAAECgYBxqm885R+fj6E7lVNwSzG2NTC4souDG+SyrPFOmp9C" +
                    "/4lyiLc6k3QwN2QhYNYI6aATDCZ57nqb" +
                    "+R0PQ99HzhUef4fLyCYLAhybmeMD7Y7EN3o+2cRNLFsKz4hw9GtqmDnT4ra8dcLk6oqjSabDPPPENw9E72cDEzqddAc" +
                    "/JQj6NQJBANeZa6Rx5LGYtVLhf7i8c7La4kbtuU3uQ" +
                    "/nbq0YaIxl421MUMGnUZU46dn9PjiDTSy7s8AKq4jZB23NlqeqjmT8CQQCiUAVAZuYOsVQZyQX6l+aA1MyK5d0Drd" +
                    "/nsfMWa2Ts0zI5akVx1p16W9AVPPpQQohKMZSMpWUiSfwXuDoccSwbAkEAya1ZVQ25SSw5Sdto921JqWA5VPIhajXc3ohPMhdXCAfW2mNDWmv1i/UCTdskb65XYQEBDu29B9YZK1xJUxVQTwJAZtuk6FIM+kSf0hNVCZ/XP88aq+7M53b5TD+1l4CJHZcc3cudpG1nsFGchMj2q4PuEG5o/9QUSe1upq1ljf6d/wJBAK2RodHBq+IrsUHDZzE1jFFn+jKWQZGz/WEKpeFnWFcaCNjtBR/SK3vZ7U1fyoUSIWK7LEdey8qVlheG78/OhtY=";
    public static final String MERCHANT_PUBLIC_KEY =
            "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCIsnp775RQSgoFahLdNQ9wPvGhGBObQpuDvqVT9hZXIuRJ6f61ol6U3hH" +
                    "+58ClW1A8HNA1" +
                    "/X51fNYVmrOqeJgBr5uUFEwaGbBdcKl6f83OGXdLIowlBMCRFgSfW06At9Cga2lBgTmrIHzl5hFpNwWf4xiu6tCR4WD4LmR" +
                    "/Zmb9pQIDAQAB";
    public static final String WSB_PUBLIC_KEY =
            "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyyPUFA1Zu2GFS2DdPL6xP5bGmPG9dFMSXpDy3GykW" +
                    "/lL4VR24UsjKs5h6g8OfSOuOze3yC4jWE/IBW5/L9xM9kGvFQK/S1zr44hMnDn+7S5MSi" +
                    "+jnZDRIrCRQJTqkwfpy39h2cQ4xlHKfbEjkkeo5ZQ0VFgRHYPr7b10l+3j8gwIDAQAB";
    public static String ENDPOINT = "https://sandbox-api-merchant.wasabicard.com";
    /**
     * 商户的标识，可自定义
     */
    public static String MERCHANT_TAG = "CWG";
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private MessageSource messageSource;
    @Autowired
    private RateLimiterUtil rateLimiterUtil;
    @Autowired
    private CardApplyRepository cardApplyRepository;
    @Autowired
    private UcardCardRepository cardRepository;
    @Autowired
    private CardActivateRepository cardActivateRepository;
    @Autowired
    private CardRechargeOrderRepository rechargeOrderRepository;
    @Autowired
    private CardMerchantUserRepository merchantUserRepository;
    @Autowired
    private CardKycAttachmentRepository kycAttachmentRepository;
    @Autowired
    private CardKycStatusRepository kycStatusRepository;
    @Autowired
    private CardOperateRepository cardOperateRepository;
    @Autowired
    private CardTypeRepository cardTypeRepository;
    @Autowired
    private CardTransacOrderRepository cardTransacOrderRepository;
    @Autowired
    private CardFileRepository cardFileRepository;
    @Autowired
    private CustomLoginRepository customLoginRepository;
    @Autowired
    private CardCountryMapper cardCountryMapper;
    @Autowired
    private MqSendService mqSendService;
    @Autowired
    private CustomInfoRepository customInfoRepository;
    @Autowired
    private CardOccupationsMapper cardOccupationsMapper;
    @Autowired
    private UcardOccupationsRepository occupationsRepository;
    @Autowired
    private UcardWalletRepository ucardWalletRepository;
    @Autowired
    private CardWalletMapper cardWalletMapper;
    @Autowired
    private AccountInfoFeignService accountInfoFeignService;
    @Autowired
    private CustomKycApproveRepository customKycApproveRepository;
    @Autowired
    private LocaleMessage localeMessage;
    @Autowired
    private KycFeignService kycFeignService;
    @Autowired
    private SysKycConfigService sysKycConfigService;
    @Autowired
    private CardApplyMapper cardApplyMapper;
    @Autowired
    private UcardWalletRecordRepository ucardWalletRecordRepository;
    @Autowired
    private CardWalletRecordMapper walletRecordMapper;
    @Autowired
    private CardPermissionRepository permissionRepository;
    @Autowired
    private CardPermissionMapper permissionMapper;
    @Autowired
    private CardOperateMapper operateMapper;
    @Autowired
    private EmailService emailService;
    @Autowired
    private CardKycStatusMapper kycStatusMapper;
    @Autowired
    private CustomKycImageRepository customKycImageRepository;
    @Autowired
    private SysReasonsRefusalRepository sysReasonsRefusalRepository;
    @Autowired
    private CardPasswordResetRepository passwordResetRepository;
    @Autowired
    private PushMessageFeignService pushMessageFeignService;
    @Autowired
    private CustomFileRepository customFileRepository;
    @Autowired
    private CardMerchantUserMapper cardMerchantUserMapper;
    @Autowired
    private CardAuthorizationFeeRepository authorizationFeeRepository;
    @Autowired
    private CardTypeConfigRepository cardTypeConfigRepository;
    @Autowired
    private CardTypeMapper cardTypeMapper;
    @Autowired
    private CardIdTypeConfigMapper cardIdTypeConfigMapper;
    @Autowired
    private CardIdTypeConfigRepository cardIdTypeConfigRepository;
    @Autowired
    private CardGlobalAvailableBankCitiesRepository globalAvailableBankCitiesRepository;
    @Autowired
    private CardGlobalCurrenciesFieldAvailableRepository globalCurrenciesFieldAvailableRepository;
    @Autowired
    private CardGlobalCurrenciesFieldRepository globalCurrenciesFieldRepository;
    @Autowired
    private CardGlobalCurrenciesRepository globalCurrenciesRepository;
    @Autowired
    private CardGlobalExchangeRateRepository globalExchangeRateRepository;
    @Autowired
    private CardGlobalOrderDataAvailableRepository globalOrderDataAvailableRepository;
    @Autowired
    private CardGlobalOrderDataRepository globalOrderDataRepository;
    @Autowired
    private CardGlobalOrderRepository globalOrderRepository;
    @Autowired
    private CardGlobalStatesRepository globalStatesRepository;
    @Autowired
    private CardGlobalStatesMapper globalStatesMapper;
    @Autowired
    private CardGlobalCurrenciesMapper globalCurrenciesMapper;
    @Autowired
    private UcardCardMapper cardMapper;
    @Autowired
    private CardGlobalAvailableBankCitiesMapper globalAvailableBankCitiesMapper;
    @Autowired
    private CardGlobalCurrenciesFieldAvailableMapper globalCurrenciesFieldAvailableMapper;
    @Autowired
    private Environment environment;
    @Autowired
    private CardRechargeOrderMapper rechargeOrderMapper;
    @Autowired
    private CardTransacOrderMapper cardTransacOrderMapper;
    @Autowired
    private CardGlobalCurrenciesFieldMapper globalCurrenciesFieldMapper;
    @Autowired
    private UcardCardNumberRepository cardNumberRepository;
    @Autowired
    private UcardCardNumberMapper cardNumberMapper;
    @Autowired
    private CardGlobalOrderMapper globalOrderMapper;
    @Autowired
    private CardFileMapper cardFileMapper;
    @Autowired
    private CardMerchantUserMapper merchantUserMapper;
    @Autowired
    private CardGlobalFeeRateConfigRepository globalFeeRateConfigRepository;
    @Autowired
    private CardBlockchainConfigMapper blockchainConfigMapper;
    @Autowired
    private CardBlockchainConfigRepository blockchainConfigRepository;
    @Autowired
    private RedisService redisService;
    @Autowired
    private UcardGlobalReceiverInfoRepository globalReceiverInfoRepository;
    @Autowired
    private CardBlockchainRateConfigMapper blockchainRateConfigMapper;
    @Autowired
    private CardBlockchainRateConfigRepository blockchainRateConfigRepository;
    @Autowired
    private CardCryptoCallbackRecordMapper cryptoCallbackRecordMapper;
    @Autowired
    private FIleFeignService fIleFeignService;
    @Autowired
    private CardWithdrawRepository withdrawRepository;
    @Autowired
    private CardWithdrawMapper withdrawMapper;
    @Autowired
    private Card3dsTransactionMapper card3dsTransactionMapper;
    @Autowired
    private Card3dsTransactionRepository card3dsTransactionRepository;

    @Value("${web.upload-path:./upload}")
    private String UPLOAD;

    public static void main1(String[] args) throws Exception {
        //全球速汇-查询支持的币种(有数据)
//        BaseResultDto<List<WasabiAvailableCurrenciesDto>> availableCurrencies = getAvailableCurrencies();
//        System.out.println(JSON.toJSONString(availableCurrencies.getData()));
        //全球速汇-查询省份/州列表(有数据)
//        BaseResultDto<List<WasabiStatesDto>> states = getStates();
//        System.out.println(JSON.toJSONString(states.getData()));
        //全球速汇-币种发送交易需要的字段(有数据)
        WasabiAvailableFieldParamsEntity fieldParamsEntity = new WasabiAvailableFieldParamsEntity();
        fieldParamsEntity.setPayoutCurrency("CNY");
        fieldParamsEntity.setTransferType("3");
        fieldParamsEntity.setPayoutMethod("1");
//        WasabiAvailableFieldParamsDto fieldParamsDto = getAvailableFieldParams(fieldParamsEntity).getData();
//        System.out.println(JSON.toJSONString(fieldParamsDto));
        //全球速汇-银行城市名单(有数据)
        WasabiAvailableBankCitiesEntity bankCitiesEntity = new WasabiAvailableBankCitiesEntity();
        bankCitiesEntity.setPayoutCurrency("AUD");
        bankCitiesEntity.setParentCodeId("Australian Capital Territory");
//        List<WasabiMethodDto> data = getAvailableBankCities(bankCitiesEntity).getData();
//        System.out.println(JSON.toJSONString(data));
        //全球速汇-法币汇率(有数据)
//        List<WasabiExchangeRateDto> rateDtoList = getExchangeRate().getData();
//        System.out.println(JSON.toJSONString(rateDtoList));


        //全球速汇-发送交易
//        transactionTransfer();
        //全球速汇-交易记录查询
//        getTransactionList();
        //全球速汇-交易详情查询
//        getTransactionDetail();
        //全球速汇-提交合规性检查资料
//        transactionSubmitRfi();
    }

    /**
     * 校验正则表达式格式
     *
     * @param regexp 待校验的正则表达式
     * @return 是否符合要求的格式
     */
    private static boolean isValidRegexpFormat(String regexp) {
        try {
            Pattern.compile(regexp);
            return true;
        } catch (PatternSyntaxException e) {
            return false;
        }
    }

    public static WasabiConfig getConfig() throws ServiceException {
        WasabiConfig config = new WasabiConfig();
        config.setApiUrl(ENDPOINT);
        config.setApiKey(MERCHANT_API_KEY);
        config.setPrivateKey(MERCHANT_PRIVATE_KEY);
        config.setPublicKey(MERCHANT_PUBLIC_KEY);
        config.setWsbPublicKey(WSB_PUBLIC_KEY);
        config.setMerchantTag(MERCHANT_TAG);
        config.setRegionUrl("/merchant/core/mcb/common/region");
        config.setCityUrl("/merchant/core/mcb/common/city");
        config.setCityV2Url("/merchant/core/mcb/common/v2/city");
        config.setMobileAreaUrl("/merchant/core/mcb/common/mobileAreaCode");
        config.setFileUploadUrl("/merchant/core/mcb/common/file/upload");
        config.setWorkSubmitUrl("/merchant/core/mcb/work/submit");
        config.setWorkListUrl("/merchant/core/mcb/work/list");
        config.setAccountInfoUrl("/merchant/core/mcb/account/info");
        config.setWalletDepositUrl("/merchant/core/mcb/account/walletDeposit");
        config.setWalletDepositTransactionUrl("/merchant/core/mcb/account/walletDepositTransaction");
        config.setCardTypesUrl("/merchant/core/mcb/card/v2/cardTypes");
        config.setOpenCardUrl("/merchant/core/mcb/card/openCard");
        config.setActiveCardUrl("/merchant/core/mcb/card/physicalCard/activeCard");
        config.setCardInfoUrl("/merchant/core/mcb/card/info");
        config.setCardBalanceInfoUrl("/merchant/core/mcb/card/balanceInfo");
        config.setCardFreezeV2Url("/merchant/core/mcb/card/v2/freeze");
        config.setCardUnfreezeV2Url("/merchant/core/mcb/card/v2/unfreeze");
        config.setCardDepositUrl("/merchant/core/mcb/card/deposit");
        config.setUpdatePinUrl("/merchant/core/mcb/card/physicalCard/updatePin");
        config.setCardTransactionUrl("/merchant/core/mcb/card/transaction");
        config.setCardAuthTransactionUrl("/merchant/core/mcb/card/authTransaction");
        config.setThird3dsTransactionUrl("/merchant/core/mcb/card/third3dsTransaction");
        config.setHolderOccupationsUrl("/merchant/core/mcb/card/holder/occupations");
        config.setHolderCreateV2Url("/merchant/core/mcb/card/holder/v2/create");
        config.setHolderUpdateV2Url("/merchant/core/mcb/card/holder/v2/update");
        config.setHolderQueryUrl("/merchant/core/mcb/card/holder/query");
        config.setCardSensitiveUrl("/merchant/core/mcb/card/sensitive");
        config.setCardWithdrawUrl("/merchant/core/mcb/card/withdraw");
        config.setCardCancelUrl("/merchant/core/mcb/card/cancel");
        config.setAvailableCurrenciesUrl("/merchant/core/mcb/gt/template/availableCurrencies");
        config.setAvailableFieldParamsUrl("/merchant/core/mcb/gt/template/availableFieldParams");
        config.setStatesUrl("/merchant/core/mcb/gt/template/states");
        config.setAvailableBankCitiesUrl("/merchant/core/mcb/gt/template/availableBankCities");
        config.setExchangeRateUrl("/merchant/core/mcb/gt/transaction/exchangeRate");
        config.setTransactionTransferUrl("/merchant/core/mcb/gt/transaction/transfer");
        config.setTransactionListUrl("/merchant/core/mcb/gt/transaction/list");
        config.setTransactionDetailUrl("/merchant/core/mcb/gt/transaction/detail");
        config.setSubmitRfiUrl("/merchant/core/mcb/gt/transaction/submitRfi");
        return config;
    }

    public static void verifySignature(TwoTuple<String, String> result, String publicKey) throws Exception {
        ResultData resultData = JSON.parseObject(result.getSecond(), ResultData.class);
        if (resultData.isSuccess()) {
            boolean verify = RsaUtils.verify(result.getSecond(), publicKey, result.getFirst());
            log.info("验证签名:{}", verify);
        }
    }

    public WasabiConfig getWasabiConfig() throws ServiceException {
        SysConfigTable sysConfigTable = sysConfigService.getByCode(Constants.WASABI_CARD);
        if (sysConfigTable == null) {
            throw ServiceException.exception(Constants.SYSTEM_ERROR);
        }
        WasabiConfig wasabiConfig = JSON.parseObject(sysConfigTable.getValue(), WasabiConfig.class);
        return wasabiConfig;
    }

    /**
     * 获取国家/地区列表
     */
    @Override
    public BaseResultDto<List<WasabiRegionDto>> getRegionList() throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getRegionUrl(), config);
            log.info("result:{}", result);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<List<WasabiRegionDto>> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<List<WasabiRegionDto>>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }

            List<WasabiRegionDto> list = resultData.getData();
            return BaseResultDto.success(list);
        } catch (Exception e) {
            log.error("获取国家/地区列表失败", e);
            throw new ServiceException("获取国家/地区列表失败");
        }
    }

    /**
     * 更新国家、地区、手机区号
     */
    @Override
    public void cardUpdateCountry() throws ServiceException {
        cardCountryMapper.deleteByCardCategory(Constants.CARD_CATEGORY_WASABI);

        List<CardCountryTable> tables = new ArrayList<>();
        Date now = new Date();
        // 获取国家地区列表
        List<WasabiRegionDto> wasabiRegionDtos = getRegionList().getData();
        // 获取手机区号列表
        List<WasabiMobileAreaCodeDto> areaCodeDtos = getMobileAreaCode().getData();
        Map<String, String> areaCodeMap = areaCodeDtos.stream()
                .collect(Collectors.toMap(WasabiMobileAreaCodeDto::getAreaCode, WasabiMobileAreaCodeDto::getCode
                ));
        // 获取城市信息
        WasabiCityEntity entity = new WasabiCityEntity();
        List<WasabiCityDto> cityDtos = getCityList(entity).getData();
        for (WasabiCityDto cityDto : cityDtos) {
            CardCountryTable table = new CardCountryTable();
            table.setCode(cityDto.getCode());
            table.setStandardCode(null);
            table.setEnName(cityDto.getName());
            table.setPcode(cityDto.getCountry());
            table.setCardCategory(Constants.CARD_CATEGORY_WASABI);
            table.setAddTime(now);
            tables.add(table);
        }
        for (WasabiRegionDto regionDto : wasabiRegionDtos) {
            CardCountryTable table = new CardCountryTable();
            table.setCode(regionDto.getCode());
            table.setStandardCode(regionDto.getStandardCode());
            table.setEnName(regionDto.getName());
            table.setAreaCode(areaCodeMap.get(regionDto.getCode()));
            table.setCardCategory(Constants.CARD_CATEGORY_WASABI);
            table.setAddTime(now);
            tables.add(table);
        }
        List<List<CardCountryTable>> lists = ListSplitUtil.split(tables);
        for (List<CardCountryTable> list : lists) {
            cardCountryMapper.batchSave(list);
        }
    }

    /**
     * 获取城市列表
     */
    @Override
    public BaseResultDto<List<WasabiCityDto>> getCityList(WasabiCityEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getCityUrl(),
                    JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<List<WasabiCityDto>> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<List<WasabiCityDto>>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }

            List<WasabiCityDto> list = resultData.getData();
            return BaseResultDto.success(list);
        } catch (Exception e) {
            log.error("获取城市列表失败", e);
            throw new ServiceException("获取城市列表失败");
        }
    }

    /**
     * 获取城市列表v2
     */
    @Override
    public BaseResultDto<List<WasabiCityV2Dto>> getCityV2List(WasabiCityEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getCityV2Url(),
                    JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<List<WasabiCityV2Dto>> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<List<WasabiCityV2Dto>>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }

            List<WasabiCityV2Dto> list = resultData.getData();
            return BaseResultDto.success(list);
        } catch (Exception e) {
            log.error("获取城市列表v2失败", e);
            throw new ServiceException("获取城市列表v2失败");
        }
    }

    /**
     * 获取手机区号列表
     */
    @Override
    public BaseResultDto<List<WasabiMobileAreaCodeDto>> getMobileAreaCode() throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getMobileAreaUrl(),
                    config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<List<WasabiMobileAreaCodeDto>> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<List<WasabiMobileAreaCodeDto>>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }

            List<WasabiMobileAreaCodeDto> list = resultData.getData();
            return BaseResultDto.success(list);
        } catch (Exception e) {
            log.error("获取手机区号列表失败", e);
            throw new ServiceException("获取手机区号列表失败");
        }
    }

    /**
     * 文件上传
     */
    public String uploadFile(String path) throws Exception {
        WasabiConfig config = getWasabiConfig();
        File file = FileProcessUtil.getFile(UPLOAD + path);
        Map<String, String> paramMap = new HashMap<>();
        //使用默认值：card
        paramMap.put("category", "card");

        TwoTuple<String, String> result = WasabiCommonUtil.postFile(config.getApiUrl() + config.getFileUploadUrl(),
                file, paramMap, config);
        verifySignature(result, config.getWsbPublicKey());
        ResultData<WasabiFileUploadDto> resultData = JSON.parseObject(result.getSecond(),
                new TypeReference<ResultData<WasabiFileUploadDto>>() {
                }
        );
        if (!resultData.isSuccess()) {
            throw new ServiceException(resultData.getMsg());
        }

        WasabiFileUploadDto dto = resultData.getData();
        return dto.getFileId();
    }

    /**
     * 文件校验
     */
    @Override
    public void validatedFile(MultipartFile file) throws ServiceException {
        int size = 20;
        String regex = "jpg|png|pdf";
        //验证文件大小
        if (file.getSize() > size * 1000 * 1024) {
            throw new ServiceException(localeMessage.getMessage(Constants.UPLOAD_FILE_SIZE_MAX) + size + "MB");
        }
        //文件格式
        String fileName = file.getOriginalFilename().toLowerCase();
        if (!fileName
                .substring(fileName.lastIndexOf(".") + 1)
                .matches(regex)) {
            throw ServiceException.exception(localeMessage.getMessage(Constants.UPLOAD_FILE_REGEX,
                    Constants.UPLOAD_FILE_REGEX) + " " + regex);

        }
    }

    /**
     * 提交工单
     */
    @Override
    public BaseResultDto<WasabiWorkSubmitAndListDto> workSubmit(WasabiWorkSubmitEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getWorkSubmitUrl(),
                    JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiWorkSubmitAndListDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiWorkSubmitAndListDto>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }

            WasabiWorkSubmitAndListDto dto = resultData.getData();
            return BaseResultDto.success(dto);
        } catch (Exception e) {
            log.error("提交工单失败", e);
            throw new ServiceException("提交工单失败");
        }
    }

    /**
     * 获取工单列表
     */
    @Override
    public BaseResultDto<List<WasabiWorkSubmitAndListDto>> workList(WasabiWorkListEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getWorkListUrl(),
                    JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData resultData = JSON.parseObject(result.getSecond(), ResultData.class);
            JSONObject data = JSON.parseObject(resultData.getData().toString());
            JSONArray records = data.getJSONArray("records");
            List<WasabiWorkSubmitAndListDto> dtos = records.toJavaList(WasabiWorkSubmitAndListDto.class);
            return BaseResultDto.success(dtos);
        } catch (Exception e) {
            log.error("获取工单列表失败", e);
            throw new ServiceException("获取工单列表失败");
        }
    }

    /**
     * 获取账户资产信息列表
     */
    @Override
    public BaseResultDto<List<WasabiAccountInfoDto>> queryMerchantAccount() throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getAccountInfoUrl(),
                    config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<List<WasabiAccountInfoDto>> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<List<WasabiAccountInfoDto>>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }

            List<WasabiAccountInfoDto> list = resultData.getData();
            return BaseResultDto.success(list);
        } catch (Exception e) {
            log.error("获取账户资产信息列表失败", e);
            throw new ServiceException("获取账户资产信息列表失败");
        }
    }

    /**
     * 钱包充值
     */
    @Override
    public BaseResultDto<WasabiWalletDepositDto> walletDeposit(WasabiWalletDepositEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getWalletDepositUrl()
                    , JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiWalletDepositDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiWalletDepositDto>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }

            WasabiWalletDepositDto dto = resultData.getData();
            return BaseResultDto.success(dto);
        } catch (Exception e) {
            log.error("钱包充值失败", e);
            throw new ServiceException("钱包充值失败");
        }
    }

    /**
     * 获取钱包充值记录列表
     */
    @Override
    public ResultWithPagerDto<WasabiWalletDepositTransactionDto> getWalletDepositTransactionList(WasabiWalletDepositTransactionEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();

        try {
            entity.setPageNum((long) entity.getPage().getCurrent());
            entity.setPageSize((long) entity.getPage().getRow());
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getWalletDepositTransactionUrl(),
                            JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData resultData = JSON.parseObject(result.getSecond(), ResultData.class);

            JSONObject data = JSON.parseObject(resultData.getData().toString());
            Integer total = data.getInteger("total");
            PageDto pageDto = PageDto.format(entity, total);
            if (total == null || total <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }
            JSONArray records = data.getJSONArray("records");
            List<WasabiWalletDepositTransactionDto> dtos = records.toJavaList(WasabiWalletDepositTransactionDto.class);
            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("获取钱包充值记录列表失败", e);
            throw new ServiceException("获取钱包充值记录列表失败");
        }
    }

    /**
     * 获取并更新支持的卡BIN列表
     */
    @Override
    public void saveCardTypeList() throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getCardTypesUrl(),
                    config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<List<WasabiCardTypesDto>> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<List<WasabiCardTypesDto>>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                throw new ServiceException(resultData.getMsg());
            }
            cardTypeMapper.deleteByCardCategory(Constants.CARD_CATEGORY_WASABI);
            List<WasabiCardTypesDto> list = resultData.getData();
            List<CardTypeTable> tables = new ArrayList<>();
            Date now = new Date();
            for (WasabiCardTypesDto cardTypeDto : list) {
                CardTypeTable table = new CardTypeTable();
                BeanUtils.copyProperties(cardTypeDto, table);
                table.setRisk(String.join(",", cardTypeDto.getRisk()));
                table.setSupport(String.join(",", cardTypeDto.getSupport()));
                table.setSupportHolderAreaCode(String.join(",", cardTypeDto.getSupportHolderAreaCode()));
                table.setSupportHolderRegin(String.join(",", cardTypeDto.getSupportHolderRegin()));
                if ("Physical".equals(cardTypeDto.getType())) {
                    table.setEnableStatus(1);
                } else {
                    table.setEnableStatus(0);
                }
                WasabiCardTypeHolderMetadataDto holderMetadataDto = cardTypeDto.getMetadata();
                BeanUtils.copyProperties(holderMetadataDto, table);
                table.setCardTypeId(cardTypeDto.getCardTypeId().intValue());
                table.setCardCategory(Constants.CARD_CATEGORY_WASABI);
                table.setAddTime(now);
                tables.add(table);
            }
            cardTypeMapper.batchSave(tables);
        } catch (Exception e) {
            log.error("获取支持的卡BIN列表失败", e);
            throw new ServiceException("获取支持的卡BIN列表失败");
        }
    }

    /**
     * 用户获取卡片类型
     */
    @Override
    public List<CardTypeUserDto> getCardTypeList(CardTypeEntity entity) throws ServiceException {
        List<CardTypeDto> cardTypeList = cardTypeMapper.getByEnableStatus();
        CardMerchantUserTable userTable = merchantUserRepository.findByCId(entity.getCId());
        List<CardTypeUserDto> dtos = new ArrayList<>();
        for (CardTypeDto typeDto : cardTypeList) {
            CardTypeUserDto dto = new CardTypeUserDto();
            BeanUtils.copyProperties(typeDto, dto);
            BigDecimal cardFee = queryCardFee(userTable, typeDto.getCardTypeId());
            dto.setCardFee(cardFee);
            if (userTable.getCardRechargeFeeRate() != null) {
                dto.setRechargeFeeRate(userTable.getCardRechargeFeeRate());
            }
            entity.setCardTypeId(dto.getCardTypeId());
            CardTypeUserDto cardTypeStatus = cardMerchantUserMapper.getCardTypeStatus(entity);
            if (null != cardTypeStatus) {
                dto.setHolderStatus(cardTypeStatus.getHolderStatus());
                dto.setApplyStatus(cardTypeStatus.getApplyStatus());
                dto.setCardStatus(cardTypeStatus.getCardStatus());
            }
            dtos.add(dto);
        }
        return dtos;
    }

    /**
     * 商户用户注册
     *
     * @param entity 注册请求参数
     * @return 注册结果
     * @throws ServiceException 服务异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto registerMerchantUser(CardMerchantUserEntity entity) throws ServiceException {
        CardMerchantUserTable ctable =
                merchantUserRepository.findFirstByCustomIdAndCardCategory(entity.getCustomId(),
                        Constants.CARD_CATEGORY_WASABI);
        if (ctable != null) {
            return BaseResultDto.error("用户已注册");
        }
        CardMerchantUserTable etable = merchantUserRepository.getByEmailAndCardCategory(entity.getEmail(),
                Constants.CARD_CATEGORY_WASABI);
        if (etable != null) {
            return BaseResultDto.error("邮箱已注册");
        }
        CardMerchantUserTable ptable = merchantUserRepository.getByMobileAndCardCategory(entity.getMobile(),
                Constants.CARD_CATEGORY_WASABI);
        if (ptable != null) {
            return BaseResultDto.error("手机号已注册");
        }

        int nameLength = entity.getLastName().length() + entity.getFirstName().length();
        if (nameLength > 23 || nameLength < 2) {
            return BaseResultDto.error(Constants.PARAMETER_ERROR);
        }
        String code = entity.getCountry();
        CardIdTypeConfigTable idTypeConfigTable = cardIdTypeConfigRepository.findByCode(code);
        if (idTypeConfigTable != null) {
            List<String> idTypes;
            String idType = idTypeConfigTable.getIdType();
            if (StringUtils.isNotEmpty(idType)) {
                if (idType.contains(",")) {
                    idTypes = Arrays.asList(idType.split(","));
                } else {
                    idTypes = Collections.singletonList(idType);
                }
                if (!idTypes.contains(entity.getIdType())) {
                    return BaseResultDto.error(Constants.ID_TYPE_ERROR);
                }
            }
        }

        CardMerchantUserTable table = new CardMerchantUserTable();
        BeanUtils.copyProperties(entity, table);
        table.setMailingCountry(entity.getCountry());
        table.setMailingTown(entity.getTown());
        table.setMailingPostCode(entity.getPostCode());
        table.setMailingAddress(entity.getAddress());
        table.setMailingAddressCn(entity.getAddressCn());
        table.setMailingMobile(entity.getMobile());
        table.setMailingAreaCode(entity.getAreaCode());
        table.setUniqueId(UUID.randomUUID().toString().replace("-", ""));
        table.setAddTime(new Date());
        table.setCardCategory(Constants.CARD_CATEGORY_WASABI);
        table.setAuthStatus(0);
        merchantUserRepository.save(table);

        CardKycStatusTable kycStatusTable = new CardKycStatusTable();
        kycStatusTable.setUniqueId(table.getUniqueId());
        kycStatusTable.setStatus(1);
        kycStatusTable.setCardCategory(Constants.CARD_CATEGORY_WASABI);
        kycStatusTable.setAddTime(new Date());
        kycStatusRepository.save(kycStatusTable);

        CardOperateTable operateTable = new CardOperateTable();
        operateTable.setOperateUser(entity.getOperateUser());
        operateTable.setOperateType(1);
        operateTable.setStatus(Constants.SUCCESS);
        operateTable.setAddTime(new Date());
        operateTable.setUniqueId(table.getUniqueId());
        cardOperateRepository.save(operateTable);

        return BaseResultDto.success(Constants.SUCCESS, table.getId());

    }

    /**
     * 商户用户信息更新
     *
     * @param entity 更新请求参数
     * @return 更新结果
     * @throws ServiceException 服务异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto updateMerchantUser(CardMerchantUserEntity entity) throws ServiceException {
        CardMerchantUserTable etable = merchantUserRepository.getByEmailAndCardCategory(entity.getEmail(),
                Constants.CARD_CATEGORY_WASABI);
        if (etable != null && !entity.getUniqueId().equals(etable.getUniqueId())) {
            return BaseResultDto.error("邮箱已存在");
        }
        CardMerchantUserTable ptable = merchantUserRepository.getByMobileAndCardCategory(entity.getMobile(),
                Constants.CARD_CATEGORY_WASABI);
        if (ptable != null && !entity.getUniqueId().equals(ptable.getUniqueId())) {
            return BaseResultDto.error("手机号已存在");
        }
        int nameLength = entity.getLastName().length() + entity.getFirstName().length();
        if (nameLength > 23 || nameLength < 2) {
            return BaseResultDto.error(Constants.PARAMETER_ERROR);
        }
        String code = entity.getCountry();
        CardIdTypeConfigTable idTypeConfigTable = cardIdTypeConfigRepository.findByCode(code);
        if (idTypeConfigTable != null) {
            List<String> idTypes;
            String idType = idTypeConfigTable.getIdType();
            if (StringUtils.isNotEmpty(idType)) {
                if (idType.contains(",")) {
                    idTypes = Arrays.asList(idType.split(","));
                } else {
                    idTypes = Collections.singletonList(idType);
                }
                if (!idTypes.contains(entity.getIdType())) {
                    return BaseResultDto.error(Constants.ID_TYPE_ERROR);
                }
            }
        }

        try {
            CardMerchantUserTable table =
                    merchantUserRepository.findFirstByCustomIdAndCardCategory(entity.getCustomId(),
                            Constants.CARD_CATEGORY_WASABI);
            BeanUtils.copyProperties(entity, table);
            table.setMailingCountry(entity.getCountry());
            table.setMailingTown(entity.getTown());
            table.setMailingPostCode(entity.getPostCode());
            table.setMailingAddress(entity.getAddress());
            table.setMailingAddressCn(entity.getAddressCn());
            table.setMailingMobile(entity.getMobile());
            table.setMailingAreaCode(entity.getAreaCode());
            table.setModifyTime(new Date());
            merchantUserRepository.save(table);

            CardKycStatusDto kycStatusTable = kycStatusMapper.getKycApproveByUniqueId(table.getUniqueId());
            if (kycStatusTable == null) {
                return BaseResultDto.error("审核记录不存在");
            }
            if (kycStatusTable.getStatus() != 1 && kycStatusTable.getStatus() != 2) {
                CardKycStatusTable statusTable = new CardKycStatusTable();
                statusTable.setUniqueId(table.getUniqueId());
                statusTable.setStatus(1);
                statusTable.setCardCategory(Constants.CARD_CATEGORY_WASABI);
                statusTable.setAddTime(new Date());
                kycStatusRepository.save(statusTable);
            }

            CardOperateTable operateTable = new CardOperateTable();
            operateTable.setOperateUser(entity.getOperateUser());
            operateTable.setOperateType(2);
            operateTable.setStatus(Constants.SUCCESS);
            operateTable.setAddTime(new Date());
            operateTable.setUniqueId(table.getUniqueId());
            cardOperateRepository.save(operateTable);

            return BaseResultDto.success();
        } catch (Exception e) {
            log.error("商户用户信息更新失败", e);
            throw new ServiceException("商户用户信息更新失败");
        }
    }

    /**
     * 更新邮寄地址
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto updateUserMailingAddress(WasabiSaveUserMailingAddressEntity entity) throws ServiceException {
        if (StringUtils.isNotEmpty(entity.getMailingAddress()) && entity.getMailingAddress().length() > 200) {
            return BaseResultDto.error("邮寄地址过长");
        }
        CardMerchantUserTable userTable = merchantUserRepository.findByUniqueId(entity.getUniqueId());
        if (userTable == null) {
            return BaseResultDto.error("用户不存在");
        }
        BeanUtils.copyProperties(entity, userTable);
        merchantUserRepository.save(userTable);
        return BaseResultDto.success();
    }

    /**
     * 查看用户详情
     *
     * @param customId
     * @return
     * @throws ServiceException
     */
    @Override
    public BaseResultDto merchantUserSingle(Long customId) throws ServiceException {
        MerchantUserDto dto = merchantUserMapper.getFirstByCustomId(customId);
        if (dto == null) {
            return BaseResultDto.success("用户不存在");
        }
        CardKycStatusDto kycTable = kycStatusMapper.getKycApproveByUniqueId(dto.getUniqueId());
        if (kycTable != null) {
            dto.setApproveStatus(kycTable.getStatus());
        }

        return BaseResultDto.success(dto);
    }

    /**
     * 手动KYC认证
     *
     * @param entity 认证请求参数
     * @return 认证结果
     * @throws ServiceException 服务异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto manualSubmitKyc(CardKycApproveEntity entity) throws ServiceException {
        CardKycStatusTable table = kycStatusRepository.findFirstById(entity.getId());
        if (table == null) {
            throw new ServiceException("认证信息不存在");
        }
        if (table.getStatus().equals(entity.getStatus())) {
            return BaseResultDto.success();
        }
        if (1 != table.getStatus()) {
            throw new ServiceException("用户已认证");
        }

        BeanUtils.copyProperties(entity, table);
        table.setModifyTime(new Date());
        kycStatusRepository.save(table);

        String status = null;
        CardMerchantUserTable merchantUserTable = merchantUserRepository.findByUniqueId(table.getUniqueId());
        if (Constants.REJECTED_STATUS.equals(table.getStatus())) {
            merchantUserTable.setKycVerifyUrl(null);
            merchantUserTable.setApplicantId(null);
            merchantUserRepository.save(merchantUserTable);
            status = Constants.FAIL;
        } else if (Constants.PASSED_STATUS.equals(table.getStatus())) {
            UcardWalletTable walletTable = ucardWalletRepository.findFirstByCId(merchantUserTable.getCId());
            if (walletTable == null) {
                walletTable = new UcardWalletTable();
                walletTable.setCustomId(merchantUserTable.getCustomId());
                walletTable.setCId(merchantUserTable.getCId());
                walletTable.setUniqueId(merchantUserTable.getUniqueId());
                walletTable.setBalance(BigDecimal.ZERO);
                walletTable.setAddTime(new Date());
                ucardWalletRepository.save(walletTable);
            }
            status = Constants.SUCCESS;
        }
        CardOperateTable operateTable = new CardOperateTable();
        operateTable.setOperateUser(entity.getCId());
        operateTable.setOperateType(4);
        operateTable.setStatus(status);
        operateTable.setAddTime(new Date());
        operateTable.setUniqueId(merchantUserTable.getUniqueId());
        cardOperateRepository.save(operateTable);

        if (Constants.REJECTED_STATUS.equals(table.getStatus())) {
            sendUserInfoReviewFailEmail(merchantUserTable, Long.valueOf(entity.getApproveDesc()));
        } else if (Constants.PASSED_STATUS.equals(table.getStatus())) {
            sendUserInfoReviewSuccessEmail(merchantUserTable);
        }

        return BaseResultDto.success();
    }

    /**
     * 开卡申请
     */
    @Override
    public BaseResultDto applyCard(CardApplyEntity entity) throws Exception {
        // 查询用户信息 提交注册流程
        CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(entity.getUniqueId());
        if (null == userTable) {
            throw new ServiceException(Constants.INFO_NOT_FOUND);
        }
        CardKycStatusTable kycStatusTable = kycStatusRepository.getFirstByUniqueIdAndStatus(entity.getUniqueId(), 2);
        if (kycStatusTable == null) {
            return BaseResultDto.error("kyc审核未完成，请等待kyc审核通过");
        }

        // 查询是否存在开卡中的数据
        CardApplyTable table = cardApplyMapper.findByUniqueIdAndCardTypeId(entity.getUniqueId(),
                entity.getCardTypeId());
        if (table != null) {
            return BaseResultDto.error("已存在开卡申请记录");
        }

        table = new CardApplyTable();
        BeanUtils.copyProperties(entity, table);
        table.setLastName(userTable.getLastName());
        table.setFirstName(userTable.getFirstName());
        table.setAreaCode(userTable.getAreaCode());
        table.setMobile(userTable.getMobile());
        table.setMailingCountry(userTable.getMailingCountry());
        table.setMailingTown(userTable.getMailingTown());
        table.setMailingPostCode(userTable.getMailingPostCode());
        table.setEmail(userTable.getEmail());
        table.setMailingAddress(userTable.getMailingAddress());
        table.setMailingAddressCn(userTable.getMailingAddressCn());
        table.setMailingAreaCode(userTable.getMailingAreaCode());
        table.setMailingMobile(userTable.getMailingMobile());
        table.setAddTime(new Date());
        table.setTradeType(1);
        table.setApproveStatus(1);
        CardTypeTable typeTable = cardTypeRepository.findByCardTypeId(entity.getCardTypeId());
        if (typeTable.getNeedCardHolder()) {
            // 查询记录中是否有创建持卡人成功的记录
            CardApplyTable cardApplyTable =
                    cardApplyRepository.getFirstByUniqueIdAndCardTypeIdAndHolderStatus(entity.getUniqueId(),
                            entity.getCardTypeId(), "pass_audit");

            WasabiConfig config = getWasabiConfig();
            if (cardApplyTable != null) {
                table.setHolderMerchantOrderNo(cardApplyTable.getHolderMerchantOrderNo());
                table.setHolderStatus(cardApplyTable.getHolderStatus());
                table.setHolderDescription(cardApplyTable.getHolderDescription());
                table.setStatusFlowLocation(cardApplyTable.getStatusFlowLocation());
                table.setHolderId(cardApplyTable.getHolderId());
                table.setRespMsg(cardApplyTable.getRespMsg());
            } else {
                table.setHolderMerchantOrderNo(ObjectIdUtil.genSnowFlakeIdStr(config.getMerchantTag()));
            }
        }
        //手续费
        if (entity.getDiscountType() == null) {
            table.setTradeStatus(2);
            cardApplyRepository.save(table);
            sendApplyCardReviewEmail(table);
        } else if (1 == entity.getDiscountType()) {
            // 查询卡费用 用户配置->币种配置->系统配置
            BigDecimal cardFee = queryCardFee(userTable, entity.getCardTypeId());
            if (cardFee == null || cardFee.compareTo(BigDecimal.ZERO) == 0) {
                table.setTradeStatus(2);
                table.setCardFee(BigDecimal.ZERO);
                cardApplyRepository.save(table);
            } else {
                AccountInfoListEntity accountEntity = new AccountInfoListEntity();
                accountEntity.setCustomId(userTable.getCustomId());
                accountEntity.setPlatform(entity.getPlatform());
                accountEntity.setLogin(entity.getLogin());
                AccountInfoDto dto = accountInfoFeignService.accountDropdownSingle(accountEntity);

                if (BigDecimal.valueOf(dto.getBalance()).compareTo(cardFee) < 0) {
                    throw ServiceException.exception("账户余额不足");
                }

                //金额打入交易账户
                String serial = GetRandom.getOrderNo("CARD");
                table.setTradeStatus(1);
                table.setSerial(serial);
                table.setCardFee(cardFee);
                cardApplyRepository.save(table);

                FinanceBalanceAddEntity financeBalanceAddEntity = new FinanceBalanceAddEntity();
                financeBalanceAddEntity.setAmount(cardFee.negate());
                financeBalanceAddEntity.setSerial(serial);
                financeBalanceAddEntity.setLogin(entity.getLogin());
                financeBalanceAddEntity.setCallbackMq(Constants.WASABI_CARD_APPLY_CALLBACK);
                mqSendService.send(Constants.FINANCE_BALANCE_INFO, JSONObject.toJSONString(financeBalanceAddEntity));
            }

        } else if (2 == entity.getDiscountType()) {
            CustomInfoTable infoTable = customInfoRepository.findFirstById(userTable.getCustomId());
            if (infoTable.getCardApplyReduction() == null || infoTable.getCardApplyReduction() <= 0) {
                throw ServiceException.exception("开卡减免次数不足");
            }
            infoTable.setCardApplyReduction(infoTable.getCardApplyReduction() - 1);
            customInfoRepository.save(infoTable);

            table.setTradeStatus(2);
            cardApplyRepository.save(table);
            // 发邮件通知审核人员审核
            sendApplyCardReviewEmail(table);
        }
        CardOperateTable operateTable = new CardOperateTable();
        operateTable.setOperateUser(entity.getCId());
        operateTable.setOperateType(5);
        operateTable.setStatus(Constants.SUCCESS);
        operateTable.setAddTime(new Date());
        operateTable.setUniqueId(entity.getUniqueId());
        cardOperateRepository.save(operateTable);

        return BaseResultDto.success();
    }

    /**
     * 查询卡片费用
     */
    public BigDecimal queryCardFee(CardMerchantUserTable table, Integer cardTypeId) throws ServiceException {
        if (table.getCardFee() != null) {
            return table.getCardFee();
        }
        CardTypeConfigTable configTable = cardTypeConfigRepository.getByCardTypeId(cardTypeId);
        if (configTable != null) {
            if (configTable.getCardFee() != null) {
                return configTable.getCardFee();
            }
        }
        SysConfigTable applyConfigTable = sysConfigService.getByCode(Constants.WASABI_CARD_APPLY_AMOUNT);
        if (applyConfigTable == null) {
            throw ServiceException.exception(Constants.SYSTEM_ERROR);
        }
        return new BigDecimal(applyConfigTable.getValue());
    }

    /**
     * 开卡申请审批
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto cardApplyApprove(CardApplyApproveEntity entity) throws Exception {
        CardApplyTable cardApplyTable = cardApplyRepository.findFirstById(entity.getId());
        if (cardApplyTable == null) {
            throw new ServiceException("卡申请记录不存在");
        }
        cardApplyTable.setCardNumber(entity.getCardNumber());
        if (entity.getApproveStatus() == null) {
            throw new ServiceException("请选择审批结果");
        }
        if (entity.getApproveStatus() == 2) {
            cardApplyTable.setApproveStatus(2);
            WasabiConfig config = getWasabiConfig();
            createHolderAndOpenCard(cardApplyTable, config);
        } else if (entity.getApproveStatus() == 3) {
            cardApplyTable.setApproveStatus(3);
            cardApplyTable.setApproveDesc(entity.getApproveDesc());
            cardApplyTable.setTradeType(2);
            cardApplyTable.setStatus(Constants.FAIL);
            cardApplyTable.setHolderStatus("reject");

            failure(cardApplyTable);
            //审批失败给客户发邮件
            CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(cardApplyTable.getUniqueId());
            sendOpenCardApprovalFailEmail(userTable, Long.valueOf(entity.getApproveDesc()));
        }
        CardOperateTable operateTable = new CardOperateTable();
        operateTable.setOperateUser(entity.getCId());
        operateTable.setOperateType(6);
        operateTable.setStatus(Constants.SUCCESS);
        operateTable.setAddTime(new Date());
        operateTable.setUniqueId(cardApplyTable.getUniqueId());
        cardOperateRepository.save(operateTable);

        return BaseResultDto.success();
    }

    /**
     * 更新卡物流状态-1天消息处理
     */
    @JmsListener(destination = Constants.CARD_LOGISTICS_STATUS_UPDATE)
    @Transactional(rollbackFor = Exception.class)
    public void updateCardLogisticsStatus(String content) throws Exception {

        log.info(String.format("CARD_LOGISTICS_STATUS_UPDATE_ONE_HOUR,content:%s", content));
        UcardCardTable table = JSON.parseObject(content, UcardCardTable.class);
        if ("4".equals(table.getLogisticsStatus())) {
            return;
        }
        // 获取当前时间时间戳
        long now = System.currentTimeMillis();
        long time = table.getAddTime().getTime();
        // 判断两个时间戳差值是否在1天到6天之间
        long timeDiff = now - time; // 时间差（毫秒）
        long dayInMillis = 24 * 60 * 60 * 1000L;
        if (timeDiff >= dayInMillis && timeDiff < 6 * dayInMillis && !"2".equals(table.getLogisticsStatus())) {
            // 1-6天
            table.setLogisticsStatus("2");
            mqSendService.send(Constants.CARD_LOGISTICS_STATUS_UPDATE, JSONObject.toJSONString(table),
                    5 * 24 * 3600 * 1000);
        } else if (timeDiff >= 6 * dayInMillis && timeDiff < 11 * dayInMillis && !"3".equals(table.getLogisticsStatus())) {
            // 6-11天
            table.setLogisticsStatus("3");
            mqSendService.send(Constants.CARD_LOGISTICS_STATUS_UPDATE, JSONObject.toJSONString(table),
                    5 * 24 * 3600 * 1000);
        } else if (timeDiff > 11 * dayInMillis && !"4".equals(table.getLogisticsStatus())) {
            // 11天以上
            table.setLogisticsStatus("4");
        }
        cardRepository.save(table);
    }

    /**
     * 用户开卡申请详情
     */
    @Override
    public BaseResultDto cardApplyDetails(BaseEntity entity) throws ServiceException {
        if (entity.getId() == null || entity.getId() <= 0) {
            return BaseResultDto.success();
        }
        CardApplyDto dto = cardApplyMapper.findById(entity);
        return BaseResultDto.success(dto);
    }

    /**
     * 手续费扣款回调
     */
    @JmsListener(destination = Constants.WASABI_CARD_APPLY_CALLBACK)
    @Transactional(rollbackFor = Exception.class)
    public void deductionCallback(String content) throws Exception {

        log.info(String.format("WASABI_CARD_APPLY_CALLBACK,content:%s", content));
        FinanceBalanceTable entity = JSON.parseObject(content, FinanceBalanceTable.class);

        if (entity == null || StringUtils.isEmpty(entity.getSerial()) || entity.getTradeStatus() == null) {
            log.error("FINANCE_BALANCE_INFO,entity is null");
            return;
        }
        CardApplyTable table = cardApplyRepository.getBySerial(entity.getSerial());
        if (table == null) {
            log.error("CARD_APPLY_INFO,entity is null");
            return;
        }
        if (table.getTradeStatus() == 2) {
            return;
        }
        if (!table.getTradeStatus().equals(entity.getTradeStatus())) {
            // 判断手续费状态 1-处理中，2-成功，3-失败 数据库默认处理中，成功继续开卡流程，失败直接改变状态为失败
            if (entity.getTradeStatus() == 3) {
                table.setStatus(Constants.FAIL);
                table.setHolderStatus("reject");
                table.setTradeStatus(3);
            } else if (entity.getTradeStatus() == 2) {
                table.setTradeStatus(2);
                // 发邮件通知审核人员审核
                sendApplyCardReviewEmail(table);
            }
            cardApplyRepository.save(table);
        }
    }

    /**
     * 注册用户并开卡
     */
    @Transactional(rollbackFor = Exception.class)
    public void createHolderAndOpenCard(CardApplyTable table, WasabiConfig config) throws Exception {
        ResultData<WasabiUserDto> wasabiUserDto;
        WasabiUserDto userDto = new WasabiUserDto();
        CardTypeTable typeTable = cardTypeRepository.findByCardTypeId(table.getCardTypeId());
        if (typeTable.getNeedCardHolder()) {
            CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(table.getUniqueId());
            if (StringUtils.isEmpty(table.getHolderStatus())) {
                WasabiUserEntity userEntity = new WasabiUserEntity();
                BeanUtils.copyProperties(userTable, userEntity);
                userEntity.setCardTypeId(Long.valueOf(table.getCardTypeId()));
                userEntity.setMerchantOrderNo(table.getHolderMerchantOrderNo());

                // 查询开卡申请记录是否存在失败的记录 如果存在查询三方持卡人信息 如果存在信息则调用持卡人修改接口，否则创建持卡人
                CardApplyTable cardApplyTable =
                        cardApplyMapper.findByUniqueIdAndHolderIdNotNullAndHolderStatusIsReject(userTable.getUniqueId());
                boolean isUpdate = false;
                if (cardApplyTable != null) {
                    // 查询三方是否存在该持卡人信息
                    WasabiHolderQueryEntity queryEntity = new WasabiHolderQueryEntity();
                    queryEntity.setHolderId(cardApplyTable.getHolderId());
                    WasabiHolderQueryDto holderQueryDto = getHolderInfo(queryEntity, config).get(0);
                    if (holderQueryDto != null) {
                        isUpdate = true;
                        userEntity.setHolderId(cardApplyTable.getHolderId());
                    }
                }
                // 判断 B2B或B2C 模式
                if (StringUtils.isNotEmpty(typeTable.getCardHolderModel()) &&
                        "B2B".equals(typeTable.getCardHolderModel())) {
                    userEntity.setCardHolderModel("B2B");
                } else {
                    // 上传文件
                    String idFrontId = uploadFile(userTable.getIdFrontUrl());
                    String idBackId = uploadFile(userTable.getIdBackUrl());
                    String idHoldId = uploadFile(userTable.getIdHoldUrl());
                    userTable.setIdFrontId(idFrontId);
                    userTable.setIdBackId(idBackId);
                    userTable.setIdHoldId(idHoldId);
                    merchantUserRepository.save(userTable);
                    userEntity.setIdFrontId(idFrontId);
                    userEntity.setIdBackId(idBackId);
                    userEntity.setIdHoldId(idHoldId);
                    userEntity.setCardHolderModel("B2C");
                }
                if (isUpdate) {
                    userEntity.setMerchantOrderNo(null);
                    wasabiUserDto = updateHolderV2(userEntity, config);
                } else {
                    wasabiUserDto = createHolderV2(userEntity, config);
                }
                //创建持卡人报错直接失败并退费
                if (!wasabiUserDto.isSuccess()) {
                    saveCardNumberStatus(table.getCardNumber(), 1);
                    table.setHolderStatus("reject");
                    table.setStatus(Constants.FAIL);
                    table.setTradeType(2);
                    table.setNote(wasabiUserDto.getMsg());
                    failure(table);
                    return;
                }
                userDto = wasabiUserDto.getData();

                table.setStatusFlowLocation(userDto.getStatusFlowLocation());
                table.setHolderStatus(userDto.getStatus());
                table.setHolderId(userDto.getHolderId());
                table.setHolderDescription(userDto.getDescription());
                table.setRespMsg(userDto.getRespMsg());
                table.setStatus("reject".equals(userDto.getStatus()) ? Constants.FAIL : null);
            } else {
                userDto.setStatus(table.getHolderStatus());
            }
            if (StringUtils.isEmpty(table.getStatus()) &&
                    "pass_audit".equals(userDto.getStatus())) {
                // 开卡
                createHolderAfterOpenCard(config, table);
            }
        } else {
            if (StringUtils.isEmpty(table.getStatus())) {
                // 开卡
                createHolderAfterOpenCard(config, table);
            }
        }
        cardApplyRepository.save(table);

        if (Constants.FAIL.equals(table.getStatus())) {
            saveCardNumberStatus(table.getCardNumber(), 1);
            table.setTradeType(2);
            failure(table);
            return;
        }

        mqSendService.send(Constants.WASABICARD_CARD_APPLY_PROGRESS, JSON.toJSONString(table), 3 * 60 * 1000);
    }

    /**
     * 根据卡号修改状态
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveCardNumberStatus(String cardNumber, Integer status) throws ServiceException {
        UcardCardNumberTable cardNumberTable = cardNumberRepository.findByCardNumber(cardNumber);
        if (cardNumberTable == null) {
            cardNumberTable = new UcardCardNumberTable();
            cardNumberTable.setCardNumber(cardNumber);
        }
        cardNumberTable.setStatus(status);
        cardNumberRepository.save(cardNumberTable);
    }

    /**
     * 创建持卡人或开卡失败
     */
    public void failure(CardApplyTable table) throws Exception {
        if (table.getDiscountType() == null) {
            table.setTradeStatus(2);
            cardApplyRepository.save(table);
        } else if (table.getDiscountType() == 1) {
            if (table.getCardFee().compareTo(BigDecimal.ZERO) == 0) {
                table.setTradeStatus(2);
                cardApplyRepository.save(table);
            } else {
                refund(table);
            }
        } else if (table.getDiscountType() == 2) {
            CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(table.getUniqueId());
            CustomInfoTable infoTable = customInfoRepository.findFirstById(userTable.getCustomId());
            infoTable.setCardApplyReduction(infoTable.getCardApplyReduction() + 1);
            customInfoRepository.save(infoTable);
            table.setTradeStatus(2);
            cardApplyRepository.save(table);
        }
    }

    /**
     * 手续费退费
     */
    public void refund(CardApplyTable table) throws Exception {
        String refundSerial = GetRandom.getOrderNo("CARD");
        table.setTradeStatus(1);
        table.setRefundSerial(refundSerial);
        cardApplyRepository.save(table);

        FinanceBalanceAddEntity financeBalanceAddEntity = new FinanceBalanceAddEntity();
        financeBalanceAddEntity.setAmount(table.getCardFee());
        financeBalanceAddEntity.setSerial(refundSerial);
        financeBalanceAddEntity.setLogin(table.getLogin());
        financeBalanceAddEntity.setCallbackMq(Constants.WASABI_CARD_REFUND_CALLBACK);
        mqSendService.send(Constants.FINANCE_BALANCE_INFO, JSONObject.toJSONString(financeBalanceAddEntity));
    }

    /**
     * 手续费退费回调
     */
    @JmsListener(destination = Constants.WASABI_CARD_REFUND_CALLBACK)
    @Transactional(rollbackFor = Exception.class)
    public void refundCallback(String content) throws Exception {

        log.info(String.format("WASABI_CARD_APPLY_CALLBACK,content:%s", content));
        FinanceBalanceTable entity = JSON.parseObject(content, FinanceBalanceTable.class);

        if (entity == null || StringUtils.isEmpty(entity.getSerial()) || entity.getTradeStatus() == null) {
            log.error("FINANCE_BALANCE_INFO,entity is null");
            return;
        }
        CardApplyTable table = cardApplyRepository.getByRefundSerial(entity.getSerial());
        if (table == null) {
            log.error("CARD_APPLY_INFO,entity is null");
            return;
        }
        if (table.getTradeType() != 2 || table.getTradeStatus() == 2) {
            return;
        }
        if (!table.getTradeStatus().equals(entity.getTradeStatus())) {
            table.setTradeStatus(entity.getTradeStatus());
            cardApplyRepository.save(table);
        }
    }

    /**
     * 在持卡人创建完成后进行开卡
     */
    private void createHolderAfterOpenCard(WasabiConfig config, CardApplyTable table) throws Exception {
        // 开卡
        WasabiOpenCardEntity cardEntity = new WasabiOpenCardEntity();
        cardEntity.setMerchantOrderNo(ObjectIdUtil.genSnowFlakeIdStr(config.getMerchantTag()));
        cardEntity.setHolderId(table.getHolderId());
        cardEntity.setCardTypeId(Long.valueOf(table.getCardTypeId()));
        cardEntity.setAmount(table.getAmount());
        cardEntity.setCardNumber(table.getCardNumber());

        ResultData<List<WasabiOpenCardDto>> resultData = openCard(cardEntity, config);
        if (resultData.isSuccess()) {
            WasabiOpenCardDto wasabiOpenCardDto = resultData.getData().get(0);
            BeanUtils.copyProperties(wasabiOpenCardDto, table);
        } else {
            table.setStatus(Constants.FAIL);
            table.setNote(resultData.getMsg());
        }
    }

    /**
     * 卡申请进度查询
     */
    @JmsListener(destination = Constants.WASABICARD_CARD_APPLY_PROGRESS)
    @Transactional(rollbackFor = Exception.class)
    public void callbackCardApplyProgress(String content) throws Exception {
        log.info(String.format("WASABICARD_CARD_APPLY_PROGRESS,content:%s", content));
        CardApplyTable table = JSON.parseObject(content, CardApplyTable.class);
        if (null == table) {
            log.error("WASABICARD_CARD_APPLY_PROGRESS,applyTable is null");
            return;
        }

        //如果开卡成功，判断卡片是否添加
        if (!Strings.isNullOrEmpty(table.getStatus()) && Constants.SUCCESS.equals(table.getStatus())) {
            saveCardNumberStatus(table.getCardNumber(), 3);
            UcardCardTable cardTable = cardRepository.getByCardNo(table.getCardNo());
            if (cardTable == null) {
                cardTable = new UcardCardTable();
                BeanUtils.copyProperties(table, cardTable);
                cardTable.setId(null);
                cardTable.setStatus(CardStatusTypeEnum.UN_ACTIVATE.getCode());
                cardTable.setFreezeStatus(Constants.SUCCESS);
                cardTable.setFreezeType("1");
                cardTable.setAddTime(new Date());
                cardTable.setLogisticsStatus("1");
                cardRepository.save(cardTable);
                mqSendService.send(Constants.CARD_LOGISTICS_STATUS_UPDATE, JSONObject.toJSONString(cardTable),
                        24 * 60 * 60 * 1000);
                sendOpenCardSuccessEmail(cardTable);
            }
            return;
        }

        CardApplyProgressEntity entity = new CardApplyProgressEntity();
        entity.setId(table.getId());
        queryApplyProgress(entity);

    }

    /**
     * 查询开卡进度
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto queryApplyProgress(CardApplyProgressEntity request) throws Exception {
        if (request.getId() == null || request.getId() <= 0) {
            return BaseResultDto.success();
        }
        WasabiConfig config = getWasabiConfig();

        CardApplyTable table = cardApplyRepository.findFirstById(request.getId());
        if (table == null) {
            throw new ServiceException(Constants.INFO_NOT_FOUND);
        }
        if (2 != table.getTradeStatus()) {
            return BaseResultDto.success(table);
        }
        if (Constants.SUCCESS.equals(table.getStatus())) {
            return BaseResultDto.success(table);
        }
        CardTypeTable typeTable = cardTypeRepository.findByCardTypeId(table.getCardTypeId());
        if (typeTable.getNeedCardHolder()) {
            if (StringUtils.isEmpty(table.getHolderStatus())) {
                createHolderAndOpenCard(table, config);
            } else {
                // 如果持卡人未创建成功 查询持卡人最新状态
                if (!"pass_audit".equals(table.getHolderStatus())) {
                    WasabiHolderQueryEntity holderQueryEntity = new WasabiHolderQueryEntity();
                    holderQueryEntity.setPageNum(1);
                    holderQueryEntity.setPageSize(10);
                    holderQueryEntity.setMerchantOrderNo(table.getHolderMerchantOrderNo());
                    WasabiHolderQueryDto holderQueryDto = getHolderInfo(holderQueryEntity, config).get(0);
                    if (holderQueryDto == null) {
                        return BaseResultDto.success();
                    }
                    // 状态不一致更新状态，如果持卡人创建失败更新失败状态并按需退费
                    if (!holderQueryDto.getStatus().equals(table.getHolderStatus())) {
                        table.setHolderId(holderQueryDto.getHolderId());
                        table.setHolderStatus(holderQueryDto.getStatus());
                        table.setHolderDescription(holderQueryDto.getDescription());
                        cardApplyRepository.save(table);
                        if ("reject".equals(table.getHolderStatus())) {
                            saveCardNumberStatus(table.getCardNumber(), 1);
                            table.setStatus(Constants.FAIL);
                            table.setTradeType(2);
                            failure(table);
                            return BaseResultDto.success(table);
                        }
                    }
                }

                if ("pass_audit".equals(table.getHolderStatus()) && (StringUtils.isEmpty(table.getStatus()))) {
                    createHolderAfterOpenCard(config, table);
                }
            }
        } else {
            if (StringUtils.isEmpty(table.getStatus())) {
                createHolderAfterOpenCard(config, table);
            }
        }
        cardApplyRepository.save(table);

        if ("processing".equals(table.getStatus()) || "wait_process".equals(table.getStatus())) {
            // 获取卡交易记录
            WasabiTransactionEntity transactionEntity = new WasabiTransactionEntity();
            transactionEntity.setType("create");
            transactionEntity.setMerchantOrderNo(table.getMerchantOrderNo());
            List<WasabiTransactionDto> dtos = getCardTransactionList(transactionEntity);
            if (CollectionUtils.isEmpty(dtos)) {
                return BaseResultDto.success(table);
            }
            WasabiTransactionDto wasabiTransactionDto = dtos.get(0);
            if (!table.getStatus().equals(wasabiTransactionDto.getStatus())) {
                BeanUtils.copyProperties(wasabiTransactionDto, table);
                table.setModifyTime(new Date());
                cardApplyRepository.save(table);
            }
        }

        if (Constants.SUCCESS.equals(table.getStatus())) {
            saveCardNumberStatus(table.getCardNumber(), 3);
            UcardCardTable cardTable = cardRepository.getByCardNo(table.getCardNo());
            if (cardTable == null) {
                cardTable = new UcardCardTable();
                BeanUtils.copyProperties(table, cardTable);
                cardTable.setId(null);
                cardTable.setStatus(CardStatusTypeEnum.UN_ACTIVATE.getCode());
                cardTable.setFreezeType("1");
                cardTable.setFreezeStatus(Constants.SUCCESS);
                cardTable.setAddTime(new Date());
                cardTable.setLogisticsStatus("1");
                cardRepository.save(cardTable);
                sendOpenCardSuccessEmail(cardTable);
                mqSendService.send(Constants.CARD_LOGISTICS_STATUS_UPDATE, JSONObject.toJSONString(cardTable),
                        24 * 60 * 60 * 1000);
            }
        }
        if (Constants.FAIL.equals(table.getStatus())) {
            saveCardNumberStatus(table.getCardNumber(), 1);
            table.setTradeType(2);
            failure(table);
        }
        return BaseResultDto.success(table);
    }

    /**
     * 开卡
     */
    public ResultData<List<WasabiOpenCardDto>> openCard(WasabiOpenCardEntity entity, WasabiConfig config) throws ServiceException {
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getOpenCardUrl(),
                    JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<List<WasabiOpenCardDto>> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<List<WasabiOpenCardDto>>>() {
                    }
            );
            return resultData;
        } catch (Exception e) {
            log.error("开卡失败", e);
            throw new ServiceException("开卡失败");
        }
    }

    /**
     * 获取持卡人信息
     */
    @Override
    public List<WasabiHolderQueryDto> getHolderInfo(WasabiHolderQueryEntity entity, WasabiConfig config) throws ServiceException {
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getHolderQueryUrl(),
                    JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData resultData = JSON.parseObject(result.getSecond(), ResultData.class);
            JSONObject data = JSON.parseObject(resultData.getData().toString());
            JSONArray records = data.getJSONArray("records");
            List<WasabiHolderQueryDto> dtos = records.toJavaList(WasabiHolderQueryDto.class);
            return dtos;
        } catch (Exception e) {
            log.error("获取持卡人列表失败", e);
            throw new ServiceException("获取持卡人列表失败");
        }
    }

    /**
     * 激活卡
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto<WasabiActiveCardDto> activeCard(WasabiActiveCardEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        UcardCardTable table = cardRepository.findByCardNo(entity.getCardNo());
        if (table.getBlocked() != null && table.getBlocked()) {
            throw new ServiceException("该卡片已被锁定");
        }
        if (StringUtils.isNotEmpty(table.getCancelStatus()) && !Constants.FAIL.equals(table.getCancelStatus())) {
            throw new ServiceException("卡片注销状态不允许操作");
        }
        if ("wait_process".equals(table.getStatus()) || "processing".equals(table.getStatus())) {
            throw new ServiceException("该卡片正在激活中");
        }
        if (Constants.SUCCESS.equals(table.getStatus())) {
            throw new ServiceException("该卡片已激活");
        }

        entity.setMerchantOrderNo(ObjectIdUtil.genSnowFlakeIdStr(config.getMerchantTag()));
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getActiveCardUrl(),
                    JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiActiveCardDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiActiveCardDto>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }

            WasabiActiveCardDto dto = resultData.getData();

            CardActivateTable activateTable = new CardActivateTable();
            dto.setStatus("processing");
            BeanUtils.copyProperties(entity, activateTable);
            BeanUtils.copyProperties(dto, activateTable);
            activateTable.setUniqueId(table.getUniqueId());
            activateTable.setAddTime(new Date());
            cardActivateRepository.save(activateTable);

            BeanUtils.copyProperties(entity, table);
            table.setStatus("processing");
            table.setModifyTime(new Date());
            if (StringUtils.isEmpty(table.getLogisticsStatus()) ||
                    (StringUtils.isNotEmpty(table.getLogisticsStatus())) && !"4".equals(table.getLogisticsStatus())) {
                table.setLogisticsStatus("4");
            }
            cardRepository.save(table);

            CardOperateTable operateTable = new CardOperateTable();
            operateTable.setCardNo(table.getCardNo());
            operateTable.setMerchantOrderNo(entity.getMerchantOrderNo());
            operateTable.setOperateUser(entity.getCId());
            operateTable.setOperateType(8);
            operateTable.setStatus(Constants.SUCCESS);
            operateTable.setAddTime(new Date());
            operateTable.setUniqueId(table.getUniqueId());
            cardOperateRepository.save(operateTable);

            return BaseResultDto.success();
        } catch (Exception e) {
            log.error("激活卡失败", e);
            throw new ServiceException("激活卡失败");
        }
    }

    /**
     * 获取卡信息
     */
    @Override
    public WasabiCardInfoDto getCardInfo(WasabiCardInfoEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getCardInfoUrl(),
                    JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiCardInfoDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiCardInfoDto>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                log.error("获取卡信息失败,cardNo{},message{}", entity.getCardNo(), resultData.getMsg());
                return null;
            }
            WasabiCardInfoDto dto = resultData.getData();
            return dto;
        } catch (Exception e) {
            log.error("获取卡信息失败", e);
            throw new ServiceException("获取卡信息失败");
        }
    }

    /**
     * 查询卡敏感信息
     */
    @Override
    public WasabiCardSensitiveDto getCardSensitive(WasabiBalanceInfoEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getCardSensitiveUrl(),
                    JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiCardSensitiveDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiCardSensitiveDto>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                log.error("获取卡敏感信息失败,cardNo{},message{}", entity.getCardNo(), resultData.getMsg());
                return null;
            }
            WasabiCardSensitiveDto dto = resultData.getData();
            // 解密字段
            dto.setCvv(RsaUtils.decryptPrivateKey(dto.getCvv(), config.getPrivateKey()));
            dto.setExpireDate(RsaUtils.decryptPrivateKey(dto.getExpireDate(), config.getPrivateKey()));
            dto.setCardNumber(RsaUtils.decryptPrivateKey(dto.getCardNumber(), config.getPrivateKey()));
            dto.setActivateUrl(RsaUtils.decryptPrivateKey(dto.getActivateUrl(), config.getPrivateKey()));
            return dto;
        } catch (Exception e) {
            log.error("获取卡敏感信息失败", e);
            throw new ServiceException("获取卡敏感信息失败");
        }
    }

    /**
     * 获取卡余额
     */
    @Override
    public BaseResultDto<WasabiBalanceInfoDto> getBalanceInfo(WasabiBalanceInfoEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        UcardCardTable cardTable = cardRepository.getByCardNo(entity.getCardNo());
        if (cardTable != null && CardStatusTypeEnum.UN_ACTIVATE.getCode().equals(cardTable.getStatus())) {
            throw new ServiceException("卡片未激活");
        }
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getCardBalanceInfoUrl(),
                            JSON.toJSONString(entity),
                            config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiBalanceInfoDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiBalanceInfoDto>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }

            WasabiBalanceInfoDto dto = resultData.getData();
            return BaseResultDto.success(dto);
        } catch (Exception e) {
            log.error("获取卡余额失败", e);
            throw new ServiceException("获取卡余额失败");
        }
    }

    /**
     * 调整钱包余额
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto updateBalance(WasabiUpdateBalanceEntity entity) throws ServiceException {
        if (entity.getType() == null) {
            return BaseResultDto.error("请选择转入转出类型");
        }
        UcardWalletTable table = ucardWalletRepository.findFirstByCId(entity.getCId());
        if (table == null) {
            throw new ServiceException("用户钱包不存在");
        }
        if (entity.getAmount() == null || entity.getAmount().compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("金额为空");
        }
        UcardWalletRecordTable recordTable = new UcardWalletRecordTable();
        recordTable.setFee(BigDecimal.ZERO);
        recordTable.setStatus(2);
        recordTable.setAddTime(new Date());
        recordTable.setSerial(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
        recordTable.setWalletId(table.getId());
        recordTable.setCId(entity.getCId());
        recordTable.setUniqueId(table.getUniqueId());
        if (entity.getType() == 1) {
            table.setBalance(table.getBalance().add(entity.getAmount()));
            recordTable.setType(5);
            recordTable.setAmount(entity.getAmount());
        } else if (entity.getType() == 2) {
            if (table.getBalance().subtract(entity.getAmount()).compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("用户钱包余额不足");
            }
            table.setBalance(table.getBalance().subtract(entity.getAmount()));
            recordTable.setType(6);
            recordTable.setAmount(entity.getAmount().negate());
        }
        ucardWalletRepository.save(table);
        ucardWalletRecordRepository.save(recordTable);
        return BaseResultDto.success();
    }

    /**
     * 卡冻结 v2
     *
     * @author gao
     * @date 2025/8/15 14:45
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto cardFreeze(WasabiFreezeAndUnfreezeEntity entity) throws Exception {
        WasabiConfig config = getWasabiConfig();

        UcardCardTable cardTable = cardRepository.findByCardNo(entity.getCardNo());
        if (cardTable == null) {
            throw new ServiceException("卡片不存在");
        }
        if (cardTable.getBlocked() != null && cardTable.getBlocked()) {
            throw new ServiceException("该卡片已被锁定");
        }
        if (StringUtils.isNotEmpty(cardTable.getCancelStatus()) && !Constants.FAIL.equals(cardTable.getCancelStatus())) {
            throw new ServiceException("卡片注销状态不允许操作");
        }
        if (!Constants.SUCCESS.equals(cardTable.getStatus())) {
            throw new ServiceException("卡片未激活");
        }
        if ("1".equals(cardTable.getFreezeType()) && !Constants.SUCCESS.equals(cardTable.getFreezeStatus())) {
            throw new ServiceException("卡片当前状态不支持冻结");
        }
        if ("2".equals(cardTable.getFreezeType())) {
            if (Constants.SUCCESS.equals(cardTable.getFreezeStatus())) {
                throw new ServiceException("卡片已冻结");
            } else if (!Constants.FAIL.equals(cardTable.getFreezeStatus())) {
                throw new ServiceException("卡片冻结处理中");
            }
        }

        entity.setMerchantOrderNo(ObjectIdUtil.genSnowFlakeIdStr(config.getMerchantTag()));
        WasabiFreezeAndUnfreezeV2Dto dto = freezeV2(entity);

        CardOperateTable table = new CardOperateTable();
        table.setCardNo(entity.getCardNo());
        table.setMerchantOrderNo(entity.getMerchantOrderNo());
        table.setOperateUser(entity.getCId());
        table.setAddTime(new Date());
        table.setStatus(dto.getStatus());
        table.setOperateType(14);
        table.setOrderNo(dto.getOrderNo());
        table.setUniqueId(cardTable.getUniqueId());
        cardOperateRepository.save(table);

        if (Constants.FAIL.equals(dto.getStatus())) {
            cardTable.setFreezeType("1");
            cardTable.setFreezeStatus(Constants.SUCCESS);
        } else {
            cardTable.setFreezeType("2");
            cardTable.setFreezeStatus(dto.getStatus());
        }
        cardTable.setModifyTime(new Date());
        cardRepository.save(cardTable);

        return BaseResultDto.success();
    }

    /**
     * 卡解冻 v2
     *
     * @author gao
     * @date 2025/8/15 14:45
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto cardUnfreeze(WasabiFreezeAndUnfreezeEntity entity) throws Exception {

        UcardCardTable cardTable = cardRepository.findByCardNo(entity.getCardNo());
        if (cardTable == null) {
            throw new ServiceException("卡片不存在");
        }
        if (cardTable.getBlocked() != null && cardTable.getBlocked()) {
            throw new ServiceException("该卡片已被锁定");
        }
        if (StringUtils.isNotEmpty(cardTable.getCancelStatus()) && !Constants.FAIL.equals(cardTable.getCancelStatus())) {
            throw new ServiceException("卡片注销状态不允许操作");
        }
        if (!Constants.SUCCESS.equals(cardTable.getStatus())) {
            throw new ServiceException("卡片未激活");
        }
        if ("2".equals(cardTable.getFreezeType()) && !Constants.SUCCESS.equals(cardTable.getFreezeStatus())) {
            throw new ServiceException("卡片当前状态不支持冻结");
        }
        if ("1".equals(cardTable.getFreezeType())) {
            if (Constants.SUCCESS.equals(cardTable.getFreezeStatus())) {
                throw new ServiceException("卡片已解冻");
            } else if (!Constants.FAIL.equals(cardTable.getFreezeStatus())) {
                throw new ServiceException("卡片解冻处理中");
            }
        }
        WasabiConfig config = getWasabiConfig();
        entity.setMerchantOrderNo(ObjectIdUtil.genSnowFlakeIdStr(config.getMerchantTag()));
        WasabiFreezeAndUnfreezeV2Dto dto = unfreezeV2(entity);

        CardOperateTable table = new CardOperateTable();
        table.setCardNo(entity.getCardNo());
        table.setMerchantOrderNo(entity.getMerchantOrderNo());
        table.setOperateUser(entity.getCId());
        table.setAddTime(new Date());
        table.setStatus(dto.getStatus());
        table.setOperateType(15);
        table.setOrderNo(dto.getOrderNo());
        table.setUniqueId(cardTable.getUniqueId());
        cardOperateRepository.save(table);

        if (Constants.FAIL.equals(dto.getStatus())) {
            cardTable.setFreezeType("2");
            cardTable.setFreezeStatus(Constants.SUCCESS);
        } else {
            cardTable.setFreezeType("1");
            cardTable.setFreezeStatus(dto.getStatus());
        }
        cardTable.setModifyTime(new Date());
        cardRepository.save(cardTable);

        return BaseResultDto.success();
    }

    /**
     * 冻结卡-v2
     */
    public WasabiFreezeAndUnfreezeV2Dto freezeV2(WasabiFreezeAndUnfreezeEntity entity) throws Exception {
        WasabiConfig config = getWasabiConfig();

        TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getCardFreezeV2Url(),
                JSON.toJSONString(entity), config);
        verifySignature(result, config.getWsbPublicKey());
        ResultData<WasabiFreezeAndUnfreezeV2Dto> resultData = JSON.parseObject(result.getSecond(),
                new TypeReference<ResultData<WasabiFreezeAndUnfreezeV2Dto>>() {
                }
        );
        if (!resultData.isSuccess()) {
            throw new ServiceException(resultData.getMsg());
        }

        WasabiFreezeAndUnfreezeV2Dto dto = resultData.getData();
        return dto;

    }

    /**
     * 解冻卡-v2
     */
    public WasabiFreezeAndUnfreezeV2Dto unfreezeV2(WasabiFreezeAndUnfreezeEntity entity) throws Exception {
        WasabiConfig config = getWasabiConfig();
        TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getCardUnfreezeV2Url(),
                JSON.toJSONString(entity), config);
        verifySignature(result, config.getWsbPublicKey());
        ResultData<WasabiFreezeAndUnfreezeV2Dto> resultData = JSON.parseObject(result.getSecond(),
                new TypeReference<ResultData<WasabiFreezeAndUnfreezeV2Dto>>() {
                }
        );
        if (!resultData.isSuccess()) {
            throw new ServiceException(resultData.getMsg());
        }

        WasabiFreezeAndUnfreezeV2Dto dto = resultData.getData();
        return dto;
    }

    /**
     * 卡充值
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto cardDeposit(WasabiBalanceEntity entity) throws ServiceException {
        if (entity.getAmount() == null || entity.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            return BaseResultDto.error("充值金额参数异常");
        }
        UcardCardTable cardTable = cardRepository.findByCardNo(entity.getCardNo());
        if (cardTable == null) {
            throw new ServiceException("卡片不存在");
        }
        if (cardTable.getBlocked() != null && cardTable.getBlocked()) {
            throw new ServiceException("该卡片已被锁定");
        }
        if (StringUtils.isNotEmpty(cardTable.getCancelStatus()) && !Constants.FAIL.equals(cardTable.getCancelStatus())) {
            throw new ServiceException("卡片注销状态不允许操作");
        }
        if (!Constants.SUCCESS.equals(cardTable.getStatus())) {
            throw new ServiceException("卡片未激活");
        }
        if ("2".equals(cardTable.getFreezeType())) {
            throw new ServiceException("卡片冻结中，暂不支持此操作");
        }
        if ("1".equals(cardTable.getFreezeType()) && !Constants.SUCCESS.equals(cardTable.getFreezeStatus())) {
            throw new ServiceException("卡片解冻中，暂不支持此操作");
        }
        CardTypeDto cardTypeDto = cardTypeMapper.getByCardTypeId(cardTable.getCardTypeId());

        if (entity.getAmount().compareTo(cardTypeDto.getRechargeMinQuota()) < 0 || entity.getAmount().compareTo(cardTypeDto.getRechargeMaxQuota()) > 0) {
            throw new ServiceException("充值金额超过限度！");
        }
        WasabiConfig config = getWasabiConfig();
        entity.setMerchantOrderNo(ObjectIdUtil.genSnowFlakeIdStr(config.getMerchantTag()));
        CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(cardTable.getUniqueId());
        UcardWalletTable walletTable = ucardWalletRepository.findFirstByCId(userTable.getCId());
        // 查询充值手续费
        BigDecimal rechargeFee = getRechargeFee(entity.getAmount(), cardTable.getCardTypeId(), userTable);
        // 三方充值手续费
        BigDecimal fee = getFee(entity.getAmount(), cardTable.getCardTypeId());
        BigDecimal balance = null == walletTable.getBalance() ? new BigDecimal(0) : walletTable.getBalance();
        if (balance.compareTo(entity.getAmount().add(rechargeFee)) < 0) {
            return BaseResultDto.error("余额不足！");
        }

        CardRechargeOrderTable table = new CardRechargeOrderTable();
        BeanUtils.copyProperties(entity, table);
        table.setStatus("processing");
        table.setRechargeFee(rechargeFee);
        table.setFee(fee);
        table.setCurrency("USD");
        table.setApproveStatus(1);
        table.setCardNumber(cardTable.getCardNumber());
        table.setUniqueId(cardTable.getUniqueId());
        table.setAddTime(new Date());
        rechargeOrderRepository.save(table);

        walletTable.setBalance(balance.subtract(entity.getAmount().add(rechargeFee)));
        ucardWalletRepository.save(walletTable);
        UcardWalletRecordTable walletRecordTable = new UcardWalletRecordTable();
        walletRecordTable.setWalletId(walletTable.getId());
        walletRecordTable.setCId(walletTable.getCId());
        walletRecordTable.setUniqueId(walletTable.getUniqueId());
        walletRecordTable.setAmount(entity.getAmount().negate());
        walletRecordTable.setFee(rechargeFee.negate());
        walletRecordTable.setSerial(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
        walletRecordTable.setOrderNo(entity.getMerchantOrderNo());
        walletRecordTable.setStatus(1);
        walletRecordTable.setType(3);
        walletRecordTable.setAddTime(new Date());
        ucardWalletRecordRepository.save(walletRecordTable);

        CardOperateTable cardOperateTable = new CardOperateTable();
        cardOperateTable.setOperateUser(entity.getCId());
        cardOperateTable.setCardNo(cardTable.getCardNo());
        cardOperateTable.setOperateType(12);
        cardOperateTable.setStatus(Constants.SUCCESS);
        cardOperateTable.setAddTime(new Date());
        cardOperateTable.setUniqueId(cardTable.getUniqueId());
        cardOperateRepository.save(cardOperateTable);

        // 充值申请给财务发邮件审批
        sendRechargeReviewEmail(userTable, cardTable.getCardNumber(), entity.getAmount(), fee, table.getAddTime());
        return BaseResultDto.success();
    }

    /**
     * 充值审批
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto cardDepositApprove(WasabiDepositApproveEntity entity) throws Exception {
        List<Long> ids = entity.getIds();
        if (CollectionUtils.isEmpty(ids)) {
            throw new ServiceException("审核id为空");
        }
        for (Long id : ids) {
            CardRechargeOrderTable table = rechargeOrderRepository.getFirstById(id);
            if (table == null) {
                throw new ServiceException("充值订单不存在");
            }
            if (entity.getApproveStatus() == null) {
                throw new ServiceException("请选择审批结果");
            }
            if (table.getApproveStatus() == 2) {
                continue;
            }
            UcardCardTable cardTable = cardRepository.getByCardNo(table.getCardNo());
            if (cardTable.getBlocked() != null && cardTable.getBlocked()) {
                throw new ServiceException("该卡片已被锁定");
            }
            if (StringUtils.isNotEmpty(cardTable.getCancelStatus()) && !Constants.FAIL.equals(cardTable.getCancelStatus())) {
                throw new ServiceException("卡片注销状态不允许操作");
            }
            CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(table.getUniqueId());
            UcardWalletTable walletTable = ucardWalletRepository.findFirstByCId(userTable.getCId());
            UcardWalletRecordTable recordTable =
                    ucardWalletRecordRepository.findFirstByOrderNo(table.getMerchantOrderNo());

            if (entity.getApproveStatus() == 2) {
                WasabiConfig config = getWasabiConfig();
                WasabiBalanceEntity depositEntity = new WasabiBalanceEntity();
                depositEntity.setCardNo(table.getCardNo());
                depositEntity.setMerchantOrderNo(table.getMerchantOrderNo());
                depositEntity.setAmount(table.getAmount());

                TwoTuple<String, String> result =
                        WasabiCommonUtil.post(config.getApiUrl() + config.getCardDepositUrl(),
                                JSON.toJSONString(depositEntity), config);
                verifySignature(result, config.getWsbPublicKey());
                ResultData<WasabiDepositDto> resultData = JSON.parseObject(result.getSecond(),
                        new TypeReference<ResultData<WasabiDepositDto>>() {
                        }
                );
                if (!resultData.isSuccess()) {
                    return BaseResultDto.error(resultData.getMsg());
                }

                WasabiDepositDto dto = resultData.getData();

                BeanUtils.copyProperties(dto, table);
                table.setTransactionTime(DateUtil.formatTimestamp(dto.getTransactionTime()));
                table.setAddTime(new Date());
                table.setApproveStatus(2);
                table.setCurrency("USD");
                table.setReceivedCurrency("USD");
                rechargeOrderRepository.save(table);
                int status;
                if (Constants.SUCCESS.equals(dto.getStatus())) {
                    status = 2;
                    // 充值成功给客户发邮件
                    sendCardRechargeSuccessEmail(userTable, table);
                } else if (Constants.FAIL.equals(dto.getStatus())) {
                    status = 3;
                    BigDecimal balance = walletTable.getBalance().add(table.getAmount()).add(table.getFee());
                    walletTable.setBalance(balance);
                    ucardWalletRepository.save(walletTable);

                    UcardWalletRecordTable walletRecordTable = new UcardWalletRecordTable();
                    walletRecordTable.setWalletId(walletTable.getId());
                    walletRecordTable.setCId(walletTable.getCId());
                    walletRecordTable.setUniqueId(walletTable.getUniqueId());
                    walletRecordTable.setAmount(table.getAmount());
                    walletRecordTable.setFee(table.getFee());
                    walletRecordTable.setSerial(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
                    walletRecordTable.setStatus(2);
                    walletRecordTable.setType(4);
                    walletRecordTable.setAddTime(new Date());

                    ucardWalletRecordRepository.save(walletRecordTable);

                    // 充值审核失败给客户发邮件
                    sendCardRechargeReviewFailEmail(userTable, table, Long.valueOf(entity.getApproveDesc()));
                } else {
                    status = 1;
                }

                recordTable.setStatus(status);
                recordTable.setType(3);
                ucardWalletRecordRepository.save(recordTable);
            } else if (entity.getApproveStatus() == 3) {
                // 审核拒绝-退款
                BigDecimal balance = walletTable.getBalance().add(table.getAmount()).add(table.getFee());
                walletTable.setBalance(balance);
                ucardWalletRepository.save(walletTable);

                // 更新充值订单状态
                recordTable.setStatus(3);
                ucardWalletRecordRepository.save(recordTable);
                // 新增退款订单信息记录
                UcardWalletRecordTable walletRecordTable = new UcardWalletRecordTable();
                walletRecordTable.setWalletId(walletTable.getId());
                walletRecordTable.setCId(walletTable.getCId());
                walletRecordTable.setUniqueId(walletTable.getUniqueId());
                walletRecordTable.setAmount(table.getAmount());
                walletRecordTable.setFee(table.getFee());
                walletRecordTable.setSerial(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
                walletRecordTable.setStatus(2);
                walletRecordTable.setType(4);
                walletRecordTable.setAddTime(new Date());
                ucardWalletRecordRepository.save(walletRecordTable);

                table.setStatus(Constants.FAIL);
                table.setApproveStatus(3);
                rechargeOrderRepository.save(table);
            }
            CardOperateTable cardOperateTable = new CardOperateTable();
            cardOperateTable.setOperateUser(entity.getCId());
            cardOperateTable.setCardNo(table.getCardNo());
            cardOperateTable.setOperateType(13);
            cardOperateTable.setStatus(Constants.SUCCESS);
            cardOperateTable.setAddTime(new Date());
            cardOperateTable.setUniqueId(userTable.getUniqueId());
            cardOperateRepository.save(cardOperateTable);
        }
        return BaseResultDto.success();
    }

    /**
     * 卡扣款
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto cardWithdraw(WasabiWithdrawEntity entity) throws Exception {
        if (entity.getAmount().compareTo(new BigDecimal("0.01")) < 0) {
            throw new ServiceException("金额不能小于0.01");
        }

        UcardCardTable ucardCardTable = cardRepository.getFirstById(entity.getId());
        if (ucardCardTable.getBlocked() != null && ucardCardTable.getBlocked()) {
            throw new ServiceException("该卡片已被锁定");
        }
        if (StringUtils.isNotEmpty(ucardCardTable.getCancelStatus()) && !Constants.FAIL.equals(ucardCardTable.getCancelStatus())) {
            throw new ServiceException("卡片注销状态不允许操作");
        }
        if (!Constants.SUCCESS.equals(ucardCardTable.getStatus())) {
            throw new ServiceException("卡片未激活");
        }
        if ("2".equals(ucardCardTable.getFreezeType())) {
            throw new ServiceException("卡片冻结中，暂不支持此操作");
        }
        if ("1".equals(ucardCardTable.getFreezeType()) && !Constants.SUCCESS.equals(ucardCardTable.getFreezeStatus())) {
            throw new ServiceException("卡片解冻中，暂不支持此操作");
        }
        // 查询卡信息
        WasabiCardInfoEntity infoEntity = new WasabiCardInfoEntity();
        infoEntity.setCardNo(ucardCardTable.getCardNo());

        WasabiCardInfoDto cardInfo = getCardInfo(infoEntity);
        if (cardInfo == null || !"Normal".equals(cardInfo.getStatus())) {
            throw new ServiceException("卡片非正常状态，不允许调用此接口");
        }
        // 查询卡余额
        WasabiBalanceInfoEntity balanceInfoEntity = new WasabiBalanceInfoEntity();
        balanceInfoEntity.setCardNo(ucardCardTable.getCardNo());
        WasabiBalanceInfoDto balanceInfoDto = getBalanceInfo(balanceInfoEntity).getData();
        // 判断余额是否充足
        if (balanceInfoDto.getAmount().compareTo(entity.getAmount()) < 0) {
            return BaseResultDto.error("卡余额不足");
        }

        WasabiConfig config = getWasabiConfig();
        WasabiBalanceEntity depositEntity = new WasabiBalanceEntity();
        depositEntity.setCardNo(ucardCardTable.getCardNo());
        depositEntity.setMerchantOrderNo(ObjectIdUtil.genSnowFlakeIdStr(config.getMerchantTag()));
        depositEntity.setAmount(entity.getAmount());

        ResultData<WasabiDepositDto> resultData = withdraw(depositEntity, config);
        if (!resultData.isSuccess()) {
            return BaseResultDto.error(resultData.getMsg());
        }

        WasabiDepositDto dto = resultData.getData();
        CardWithdrawTable table = new CardWithdrawTable();
        BeanUtils.copyProperties(dto, table);
        table.setAmount(entity.getAmount().negate());
        table.setType("3");
        table.setUniqueId(ucardCardTable.getUniqueId());
        table.setCardNumber(ucardCardTable.getCardNumber());
        table.setAddTime(new Date());
        withdrawRepository.save(table);
        // 保存操作记录
        CardOperateTable cardOperateTable = new CardOperateTable();
        cardOperateTable.setUniqueId(ucardCardTable.getUniqueId());
        cardOperateTable.setMerchantOrderNo(dto.getMerchantOrderNo());
        cardOperateTable.setOrderNo(dto.getOrderNo());
        cardOperateTable.setCardNo(ucardCardTable.getCardNo());
        cardOperateTable.setOperateType(18);
        cardOperateTable.setOperateUser(entity.getCId());
        cardOperateTable.setStatus(dto.getStatus());
        cardOperateTable.setAddTime(new Date());
        cardOperateRepository.save(cardOperateTable);

        return BaseResultDto.success(table);
    }

    /**
     * 销卡
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto cardCancel(WasabiCancelEntity entity) throws Exception {

        UcardCardTable table = cardRepository.findByCardNo(entity.getCardNo());
        if (table == null) {
            return BaseResultDto.error(Constants.INFO_NOT_FOUND);
        }
        if (StringUtils.isNotEmpty(table.getCancelStatus()) && !Constants.FAIL.equals(table.getCancelStatus())) {
            throw new ServiceException("卡片注销状态不允许操作");
        }

        WasabiConfig config = getConfig();
        entity.setMerchantOrderNo(ObjectIdUtil.genSnowFlakeIdStr(config.getMerchantTag()));
        TwoTuple<String, String> result =
                WasabiCommonUtil.post(config.getApiUrl() + config.getCardCancelUrl(),
                        JSON.toJSONString(entity), config);
        verifySignature(result, config.getWsbPublicKey());
        ResultData<WasabiDepositDto> resultData = JSON.parseObject(result.getSecond(),
                new TypeReference<ResultData<WasabiDepositDto>>() {
                }
        );
        if (!resultData.isSuccess()) {
            return BaseResultDto.error(resultData.getMsg());
        }
        WasabiDepositDto dto = resultData.getData();
        table.setCancelStatus(dto.getStatus());
        cardRepository.save(table);
        CardApplyTable applyTable = cardApplyRepository.getByCardNo(table.getCardNo());
        if (dto.getStatus().equals(Constants.SUCCESS)) {
            applyTable.setStatus("cancel");
            cardApplyRepository.save(applyTable);
        }

        // 保存操作记录
        CardOperateTable cardOperateTable = new CardOperateTable();
        cardOperateTable.setUniqueId(table.getUniqueId());
        cardOperateTable.setMerchantOrderNo(dto.getMerchantOrderNo());
        cardOperateTable.setOrderNo(dto.getOrderNo());
        cardOperateTable.setCardNo(table.getCardNo());
        cardOperateTable.setOperateType(23);
        cardOperateTable.setOperateUser(entity.getCId());
        cardOperateTable.setStatus(dto.getStatus());
        cardOperateTable.setAddTime(new Date());
        cardOperateRepository.save(cardOperateTable);

        return BaseResultDto.success();
    }

    /**
     * 扣款
     */
    @Override
    public ResultData<WasabiDepositDto> withdraw(WasabiBalanceEntity entity, WasabiConfig config) throws Exception {
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getCardWithdrawUrl(),
                            JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiDepositDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiDepositDto>>() {
                    }
            );
            return resultData;
        } catch (Exception e) {
            log.error("卡扣款失败", e);
            throw new ServiceException("卡扣款失败");
        }
    }

    /**
     * 充值手续费
     */
    public BigDecimal getRechargeFee(BigDecimal amount, Integer cardTypeId, CardMerchantUserTable userTable) {
        BigDecimal cardRechargeFeeRate = null;
        if (userTable.getCardRechargeFeeRate() != null) {
            cardRechargeFeeRate = userTable.getCardRechargeFeeRate();
        } else {
            CardTypeDto cardTypeDto = cardTypeMapper.getByCardTypeId(cardTypeId);
            if (cardTypeDto.getRechargeFeeRate() != null && cardTypeDto.getRechargeFeeRate().compareTo(BigDecimal.ZERO) >= 0) {
                cardRechargeFeeRate = cardTypeDto.getRechargeFeeRate();
            }
        }
        if (cardRechargeFeeRate == null) {
            cardRechargeFeeRate = BigDecimal.ZERO;
        }
        BigDecimal scale = amount.setScale(0, RoundingMode.UP);
        BigDecimal rechargeFee = scale.multiply(cardRechargeFeeRate).divide(new BigDecimal("100"), 2,
                RoundingMode.HALF_UP);

        return rechargeFee;
    }

    /**
     * 三方充值手续费
     */
    public BigDecimal getFee(BigDecimal amount, Integer cardTypeId) {
        BigDecimal cardRechargeFeeRate = null;
        CardTypeTable table = cardTypeRepository.findByCardTypeId(cardTypeId);
        if (table.getRechargeFeeRate() != null && table.getRechargeFeeRate().compareTo(BigDecimal.ZERO) >= 0) {
            cardRechargeFeeRate = table.getRechargeFeeRate();
        }
        if (cardRechargeFeeRate == null) {
            cardRechargeFeeRate = BigDecimal.ZERO;
        }
        BigDecimal scale = amount.setScale(0, RoundingMode.UP);
        BigDecimal rechargeFee = scale.multiply(cardRechargeFeeRate).divide(new BigDecimal("100"), 2,
                RoundingMode.HALF_UP);

        return rechargeFee;
    }

    /**
     * 用户钱包余额
     */
    @Override
    public BaseResultDto<WalletBalanceDto> walletBalance(WalletBalanceEntity entity) throws ServiceException {
        UcardWalletTable table = ucardWalletRepository.getFirstByCId(entity.getCId());
        if (table == null) {
            table = new UcardWalletTable();
            CardMerchantUserTable userTable = merchantUserRepository.getFirstByCustomId(entity.getCustomId());
            table.setCustomId(entity.getCustomId());
            table.setCId(userTable.getCId());
            table.setUniqueId(userTable.getUniqueId());
            table.setBalance(BigDecimal.ZERO);
            table.setAddTime(new Date());
        }
        WalletBalanceDto dto = new WalletBalanceDto();
        dto.setCId(entity.getCId());
        dto.setCustomId(entity.getCustomId());
        dto.setBalance(table.getBalance());
        return BaseResultDto.success(dto);
    }

    /**
     * 用户钱包记录
     */
    @Override
    public ResultWithPagerDto<CardWalletRecordDto> getCardWalletRecordPageList(CardWalletRecordPageEntity entity) throws ServiceException {
        try {
            /**
             * 格式化时间
             */
            if (entity.getEndDate() != null) {
                entity.setEndDate(DateUtil.operationDay(entity.getEndDate(), 1));
            }
            // 获取总数
            Integer count = walletRecordMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }

            List<CardWalletRecordDto> cardlist = walletRecordMapper.pageList(entity);

            return ResultWithPagerDto.success(pageDto, cardlist);
        } catch (Exception e) {
            log.error("获取钱包记录分页列表失败", e);
            throw new ServiceException("获取钱包记录分页列表失败");
        }
    }

    /**
     * 查询充值订单
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto queryDepositOrder(CardRechargeOrderQueryEntity entity) throws Exception {

        try {
            CardRechargeOrderTable table = rechargeOrderRepository.findByMerchantOrderNo(entity.getMerchantOrderNo());
            if (table == null) {
                throw new ServiceException("订单不存在");
            }
            if (StringUtils.isNotEmpty(table.getStatus()) && Constants.SUCCESS.equals(table.getStatus())) {
                return BaseResultDto.success(table);
            }

            // 获取卡交易记录
            WasabiTransactionEntity transactionEntity = new WasabiTransactionEntity();
            transactionEntity.setType("deposit");
            transactionEntity.setMerchantOrderNo(entity.getMerchantOrderNo());
            List<WasabiTransactionDto> dtos = getCardTransactionList(transactionEntity);
            if (CollectionUtils.isEmpty(dtos)) {
                return BaseResultDto.success(table);
            }
            WasabiTransactionDto dto = dtos.get(0);
            if (dto == null || dto.getStatus() == null) {
                return BaseResultDto.success(table);
            }
            if (!table.getStatus().equals(dto.getStatus())) {
                BeanUtils.copyProperties(dto, table);
                table.setTransactionTime(DateUtil.formatTimestamp(dto.getTransactionTime()));
                table.setModifyTime(new Date());
                table.setCurrency("USD");
                table.setReceivedCurrency("USD");
                rechargeOrderRepository.save(table);
                UcardWalletRecordTable recordTable =
                        ucardWalletRecordRepository.findFirstByOrderNo(entity.getMerchantOrderNo());
                CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(table.getUniqueId());
                if (recordTable != null && StringUtils.isEmpty(table.getRechargeType())) {
                    if (Constants.SUCCESS.equals(dto.getStatus())) {
                        recordTable.setStatus(2);
                        // 充值成功给客户发邮件
                        sendCardRechargeSuccessEmail(userTable, table);
                    } else if (Constants.FAIL.equals(dto.getStatus())) {
                        UcardWalletTable walletTable = ucardWalletRepository.findFirstByCId(userTable.getCId());
                        recordTable.setStatus(3);
                        BigDecimal balance = null == walletTable.getBalance() ? new BigDecimal(0) :
                                walletTable.getBalance();
                        walletTable.setBalance(balance.add(table.getAmount()).add(table.getFee()));
                        ucardWalletRepository.save(walletTable);
                        // 新增退款订单信息记录
                        UcardWalletRecordTable walletRecordTable = new UcardWalletRecordTable();
                        walletRecordTable.setWalletId(walletTable.getId());
                        walletRecordTable.setCId(walletTable.getCId());
                        walletRecordTable.setUniqueId(walletTable.getUniqueId());
                        walletRecordTable.setAmount(table.getAmount());
                        walletRecordTable.setFee(table.getFee());
                        walletRecordTable.setSerial(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
                        walletRecordTable.setStatus(2);
                        walletRecordTable.setType(4);
                        walletRecordTable.setAddTime(new Date());
                        ucardWalletRecordRepository.save(walletRecordTable);
                    }
                    recordTable.setModifyTime(new Date());
                    ucardWalletRecordRepository.save(recordTable);
                }
            }
            return BaseResultDto.success(table);
        } catch (ServiceException e) {
            log.error("查询充值订单失败，错误信息：{}", e.getMessage());
            throw e;
        } catch (Exception e) {
            log.error("查询充值订单异常", e);
            throw new ServiceException("查询充值订单失败，请稍后重试");
        }
    }

    /**
     * 更新实体卡PIN
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto updatePin(WasabiUpdatePinEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        UcardCardTable cardTable = cardRepository.findByCardNo(entity.getCardNo());
        if (cardTable == null) {
            throw new ServiceException("卡片不存在");
        }
        if (cardTable.getBlocked() != null && cardTable.getBlocked()) {
            throw new ServiceException("该卡片已被锁定");
        }
        if (StringUtils.isNotEmpty(cardTable.getCancelStatus()) && !Constants.FAIL.equals(cardTable.getCancelStatus())) {
            throw new ServiceException("卡片注销状态不允许操作");
        }
        if (!Constants.SUCCESS.equals(cardTable.getStatus())) {
            throw new ServiceException("卡片未激活");
        }
        if (!"1".equals(cardTable.getFreezeType()) && !Constants.SUCCESS.equals(cardTable.getFreezeStatus())) {
            throw new ServiceException("当前卡状态不支持修改密码");
        }
        entity.setMerchantOrderNo(ObjectIdUtil.genSnowFlakeIdStr(config.getMerchantTag()));
        try {
            TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getUpdatePinUrl(),
                    JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiUpdatePinDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiUpdatePinDto>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }

            WasabiUpdatePinDto dto = resultData.getData();

            CardOperateTable table = new CardOperateTable();
            table.setCardNo(entity.getCardNo());
            table.setMerchantOrderNo(entity.getMerchantOrderNo());
            table.setOperateUser(entity.getCId());
            table.setAddTime(new Date());
            table.setStatus(dto.getStatus());
            table.setOperateType(16);
            table.setOrderNo(dto.getOrderNo());
            table.setUniqueId(cardTable.getUniqueId());
            cardOperateRepository.save(table);

            CardPasswordResetTable passwordResetTable = new CardPasswordResetTable();
            passwordResetTable.setCardNo(entity.getCardNo());
            passwordResetTable.setAddTime(new Date());
            passwordResetTable.setUniqueId(cardTable.getUniqueId());
            passwordResetTable.setPassword(entity.getPin());
            passwordResetTable.setMerchantOrderNo(entity.getMerchantOrderNo());
            passwordResetTable.setStatus(dto.getStatus());
            passwordResetRepository.save(passwordResetTable);

            if (Constants.SUCCESS.equals(dto.getStatus())) {
                cardTable.setPin(entity.getPin());
                cardTable.setModifyTime(new Date());
                cardRepository.save(cardTable);
                // 修改密码成功给客户发邮件
                CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(cardTable.getUniqueId());
                sendCardUpdatePasswordSuccessEmail(userTable, cardTable.getCardNumber());
            }
            return BaseResultDto.success(dto.getStatus());
        } catch (Exception e) {
            log.error("更新实体卡PIN失败", e);
            throw new ServiceException("更新实体卡PIN失败");
        }
    }

    /**
     * 获取卡交易记录
     */
    @Override
    public List<WasabiTransactionDto> getCardTransactionList(WasabiTransactionEntity entity) throws Exception {
        WasabiConfig config = getWasabiConfig();
        entity.setPageNum(1L);
        entity.setPageSize(10L);
        TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getCardTransactionUrl(),
                JSON.toJSONString(entity), config);
        verifySignature(result, config.getWsbPublicKey());

        ResultData resultData = JSON.parseObject(result.getSecond(), ResultData.class);
        if (!resultData.isSuccess()) {
            throw new ServiceException(resultData.getMsg());
        }
        JSONObject data = JSON.parseObject(resultData.getData().toString());
        Integer total = data.getInteger("total");
        if (total == null || total <= 0) {
            return Collections.emptyList();
        }
        JSONArray records = data.getJSONArray("records");
        List<WasabiTransactionDto> dtos = records.toJavaList(WasabiTransactionDto.class);

        return dtos;
    }

    /**
     * 获取卡授权交易记录列表
     */
    @Override
    public BaseResultDto<WasabiAuthTransactionDto> getAuthTransactionList(WasabiAuthTransactionEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getCardAuthTransactionUrl(),
                            JSON.toJSONString(entity)
                            , config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData resultData = JSON.parseObject(result.getSecond(), ResultData.class);

            JSONObject data = JSON.parseObject(resultData.getData().toString());
            Integer total = data.getInteger("total");

            if (total == null || total <= 0) {
                throw new ServiceException("数据不存在");
            }
            JSONArray records = data.getJSONArray("records");
            List<WasabiAuthTransactionDto> dtos = records.toJavaList(WasabiAuthTransactionDto.class);
            return BaseResultDto.success(dtos);

        } catch (Exception e) {
            log.error("获取卡授权交易记录列表失败", e);
            throw new ServiceException("获取卡授权交易记录列表失败");
        }
    }

    /**
     * 获取卡3ds交易记录
     */
    @Override
    public List<WasabiThird3dsTransactionDto> getThird3dsTransactionList(WasabiThird3dsTransactionEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            entity.setPageNum(1L);
            entity.setPageSize(10L);
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getThird3dsTransactionUrl(),
                            JSON.toJSONString(entity)
                            , config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData resultData = JSON.parseObject(result.getSecond(), ResultData.class);

            JSONObject data = JSON.parseObject(resultData.getData().toString());
            Integer total = data.getInteger("total");
            if (total == null || total <= 0) {
                return Collections.emptyList();
            }
            JSONArray records = data.getJSONArray("records");
            List<WasabiThird3dsTransactionDto> dtos = records.toJavaList(WasabiThird3dsTransactionDto.class);
            if (CollectionUtils.isNotEmpty(dtos)) {
                for (WasabiThird3dsTransactionDto dto : dtos) {
                    if (StringUtils.isNotEmpty(dto.getValues())) {
                        dto.setValues(RsaUtils.decryptPrivateKey(dto.getValues(), config.getPrivateKey()));
                    }
                }
            }
            return dtos;
        } catch (Exception e) {
            log.error("获取卡3ds交易记录失败", e);
            throw new ServiceException("获取卡3ds交易记录失败");
        }
    }

    /**
     * 获取职业信息列表
     */
    @Override
    public BaseResultDto<List<WasabiOccupationsDto>> getOccupationList() throws ServiceException {
        try {
            List<UcardOccupationsTable> tables = occupationsRepository.findAll();

            List<WasabiOccupationsDto> dtos = tables.stream()
                    .map(wasabiDto -> {
                        WasabiOccupationsDto dto = new WasabiOccupationsDto();
                        BeanUtils.copyProperties(wasabiDto, dto);
                        return dto;
                    })
                    .collect(Collectors.toList());
            return BaseResultDto.success(dtos);
        } catch (Exception e) {
            log.error("获取职业信息列表失败", e);
            throw new ServiceException("获取职业信息列表失败");
        }
    }

    /**
     * 更新职业信息列表
     */
    @Override
    public void updateOccupationList() throws ServiceException {
        cardOccupationsMapper.deleteAll();
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getHolderOccupationsUrl(), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<List<WasabiOccupationsDto>> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<List<WasabiOccupationsDto>>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                throw ServiceException.exception(resultData.getMsg());
            }
            Date now = new Date();
            List<WasabiOccupationsDto> dtos = resultData.getData();
            // 按occupation_code去重
            Map<String, WasabiOccupationsDto> uniqueMap = dtos.stream()
                    .collect(Collectors.toMap(
                            WasabiOccupationsDto::getOccupationCode,
                            dto -> dto,
                            (existing, replacement) -> existing // 保留第一个遇到的重复项
                    ));
            List<UcardOccupationsTable> tables = uniqueMap.values().stream()
                    .map(wasabiDto -> {
                        UcardOccupationsTable dto = new UcardOccupationsTable();
                        BeanUtils.copyProperties(wasabiDto, dto);
                        dto.setAddTime(now);
                        return dto;
                    })
                    .collect(Collectors.toList());
            List<List<UcardOccupationsTable>> lists = ListSplitUtil.split(tables);
            for (List<UcardOccupationsTable> list : lists) {
                cardOccupationsMapper.batchSave(list);
            }
        } catch (Exception e) {
            log.error("更新职业信息列表失败", e);
            throw new ServiceException("更新职业信息列表失败");
        }
    }

    /**
     * 创建持卡人
     */
    public ResultData<WasabiUserDto> createHolderV2(WasabiUserEntity entity, WasabiConfig config) throws Exception {
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getHolderCreateV2Url(), JSON.toJSONString(entity),
                            config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiUserDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiUserDto>>() {
                    }
            );
            return resultData;
        } catch (Exception e) {
            log.error("创建持卡人V2失败", e);
            throw new ServiceException("创建持卡人V2失败");
        }
    }

    /**
     * 修改持卡人
     */
    @Override
    public ResultData<WasabiUserDto> updateHolderV2(WasabiUserEntity entity, WasabiConfig config) throws ServiceException {
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getHolderUpdateV2Url(), JSON.toJSONString(entity),
                            config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiUserDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiUserDto>>() {
                    }
            );

            return resultData;
        } catch (Exception e) {
            log.error("修改持卡人 V2失败", e);
            throw new ServiceException("修改持卡人 V2失败");
        }
    }

    /**
     * 回调通知处理
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cardTransactionNotify(WasabiTransactionDto dto) throws Exception {
        Date now = new Date();
        switch (dto.getType()) {
            case "create"://开卡；
                CardApplyTable applyTable = cardApplyRepository.getByMerchantOrderNo(dto.getMerchantOrderNo());
                if (null == applyTable) {
                    throw new ServiceException("卡片不存在");
                }
                if (!applyTable.getStatus().equals(dto.getStatus())) {
                    BeanUtils.copyProperties(dto, applyTable);
                    cardApplyRepository.save(applyTable);
                    UcardCardTable cardTable = cardRepository.getByCardNo(dto.getCardNo());
                    if (cardTable != null) {
                        return;
                    }
                    if (Constants.SUCCESS.equals(dto.getStatus())) {
                        saveCardNumberStatus(applyTable.getCardNumber(), 3);
                        cardTable = new UcardCardTable();
                        BeanUtils.copyProperties(applyTable, cardTable);
                        cardTable.setId(null);
                        cardTable.setStatus(CardStatusTypeEnum.UN_ACTIVATE.getCode());
                        cardTable.setType(dto.getType());
                        cardTable.setSubType(dto.getSubType());
                        cardTable.setTransactionTime(dto.getTransactionTime());
                        cardTable.setFreezeStatus(Constants.SUCCESS);
                        cardTable.setFreezeType("1");
                        cardTable.setAddTime(now);
                        cardTable.setLogisticsStatus("1");
                        cardRepository.save(cardTable);
                        mqSendService.send(Constants.CARD_LOGISTICS_STATUS_UPDATE, JSONObject.toJSONString(cardTable)
                                , 24 * 60 * 60 * 1000);
                        sendOpenCardSuccessEmail(cardTable);
                    }
                    if (Constants.FAIL.equals(applyTable.getStatus())) {
                        saveCardNumberStatus(applyTable.getCardNumber(), 1);
                        applyTable.setTradeType(2);
                        failure(applyTable);
                    }
                }
                break;
            case "deposit"://充值；
                CardRechargeOrderTable table = rechargeOrderRepository.findByMerchantOrderNo(dto.getMerchantOrderNo());
                if (table == null) {
                    log.error("充值订单不存在-notifyParam : {}", JSON.toJSONString(dto));
                    throw new ServiceException("充值订单不存在");
                }
                if (Constants.SUCCESS.equals(table.getStatus()) || Constants.FAIL.equals(table.getStatus())) {
                    return;
                }
                UcardCardTable cardTable = cardRepository.getByCardNo(dto.getCardNo());
                if (!table.getStatus().equals(dto.getStatus())) {
                    BeanUtils.copyProperties(dto, table);
                    table.setTransactionTime(DateUtil.formatTimestamp(dto.getTransactionTime()));
                    table.setModifyTime(now);
                    table.setCurrency("USD");
                    table.setReceivedCurrency("USD");
                    rechargeOrderRepository.save(table);
                    CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(table.getUniqueId());
                    if (StringUtils.isEmpty(table.getRechargeType())) {
                        UcardWalletRecordTable recordTable =
                                ucardWalletRecordRepository.findFirstByOrderNo(dto.getMerchantOrderNo());
                        if (Constants.SUCCESS.equals(dto.getStatus())) {
                            if (cardTable != null && !Constants.SUCCESS.equals(cardTable.getStatus())) {
                                cardTable.setStatus(Constants.SUCCESS);
                                cardTable.setType(dto.getType());
                                cardRepository.save(cardTable);
                            }
                            recordTable.setStatus(2);
                            // 充值成功给客户发邮件
                            sendCardRechargeSuccessEmail(userTable, table);
                        } else if (Constants.FAIL.equals(dto.getStatus())) {
                            UcardWalletTable walletTable = ucardWalletRepository.findFirstByCId(userTable.getCId());
                            recordTable.setStatus(3);
                            BigDecimal balance = null == walletTable.getBalance() ? new BigDecimal(0) :
                                    walletTable.getBalance();
                            walletTable.setBalance(balance.add(table.getAmount()).add(table.getFee()));
                            ucardWalletRepository.save(walletTable);
                            // 新增退款订单信息记录
                            UcardWalletRecordTable walletRecordTable = new UcardWalletRecordTable();
                            walletRecordTable.setWalletId(walletTable.getId());
                            walletRecordTable.setCId(walletTable.getCId());
                            walletRecordTable.setUniqueId(walletTable.getUniqueId());
                            walletRecordTable.setAmount(table.getAmount());
                            walletRecordTable.setFee(table.getFee());
                            walletRecordTable.setSerial(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
                            walletRecordTable.setStatus(2);
                            walletRecordTable.setType(4);
                            walletRecordTable.setAddTime(now);
                            ucardWalletRecordRepository.save(walletRecordTable);
                        }
                        recordTable.setModifyTime(now);
                        ucardWalletRecordRepository.save(recordTable);
                    } else if ("1".equals(table.getRechargeType())) {
                        if (Constants.SUCCESS.equals(dto.getStatus())) {
                            if (cardTable != null && !Constants.SUCCESS.equals(cardTable.getStatus())) {
                                cardTable.setStatus(Constants.SUCCESS);
                                cardTable.setType(dto.getType());
                                cardRepository.save(cardTable);
                                // 激活成功发送邮件通知
                                sendActivateCardSuccessEmail(cardTable);
                            }
                        }
                    } else if ("2".equals(table.getRechargeType())) {
                        CardGlobalOrderTable orderTable =
                                globalOrderRepository.findByRefundMerchantOrderNo(dto.getMerchantOrderNo());
                        // 处理速汇退款订单
                        if (orderTable != null) {
                            if (Constants.SUCCESS.equals(dto.getStatus())) {
                                orderTable.setDeductionAccountStatus("5");
                            } else if (Constants.FAIL.equals(dto.getStatus())) {
                                orderTable.setDeductionAccountStatus("6");
                            }
                            globalOrderRepository.save(orderTable);
                        }
                    }
                }
                break;
            case "cancel"://销卡；
                UcardCardTable cancelCard = cardRepository.findByCardNo(dto.getCardNo());
                if (cancelCard == null) {
                    throw new ServiceException("卡不存在");
                }
                if (!cancelCard.getCancelStatus().equals(dto.getStatus())) {
                    cancelCard.setCancelStatus(dto.getStatus());
                    cancelCard.setSubType(dto.getSubType());
                    cancelCard.setType(dto.getType());
                    cancelCard.setRemark(dto.getRemark());
                    cancelCard.setTransactionTime(dto.getTransactionTime());
                    cancelCard.setModifyTime(now);
                    cardRepository.save(cancelCard);
                    CardApplyTable apply = cardApplyRepository.getByCardNo(dto.getCardNo());
                    apply.setStatus("cancel");
                    apply.setType(dto.getType());
                    apply.setSubType(dto.getSubType());
                    apply.setRemark(dto.getRemark());
                    apply.setModifyTime(now);
                    cardApplyRepository.save(apply);
                }
                break;
            case "Freeze"://冻结；
                UcardCardTable card = cardRepository.findByCardNo(dto.getCardNo());
                if (card == null) {
                    throw new ServiceException("卡不存在");
                }
                if ("2".equals(card.getFreezeType()) && Constants.SUCCESS.equals(card.getFreezeStatus())) {
                    throw new ServiceException("卡已冻结");
                }

                if (!card.getFreezeStatus().equals(dto.getStatus())) {
                    if (Constants.FAIL.equals(dto.getStatus())) {
                        card.setFreezeType("1");
                        card.setFreezeStatus(Constants.SUCCESS);
                    } else {
                        card.setFreezeType("2");
                        card.setFreezeStatus(dto.getStatus());
                    }
                    card.setType(dto.getType());
                    card.setRemark(dto.getRemark());
                    card.setTransactionTime(dto.getTransactionTime());
                    card.setModifyTime(now);
                    cardRepository.save(card);
                    CardOperateTable operateTable =
                            cardOperateRepository.findByMerchantOrderNo(dto.getMerchantOrderNo());
                    if (operateTable != null) {
                        operateTable.setStatus(dto.getStatus());
                        cardOperateRepository.save(operateTable);
                    }
                }
                break;
            case "UnFreeze"://解冻；
                cardTable = cardRepository.findByCardNo(dto.getCardNo());
                if (cardTable == null) {
                    throw new ServiceException("卡不存在");
                }
                if ("1".equals(cardTable.getFreezeType()) && Constants.SUCCESS.equals(cardTable.getFreezeStatus())) {
                    throw new ServiceException("卡已冻结");
                }

                if (!cardTable.getFreezeStatus().equals(dto.getStatus())) {
                    if (Constants.FAIL.equals(dto.getStatus())) {
                        cardTable.setFreezeType("2");
                        cardTable.setFreezeStatus(Constants.SUCCESS);
                    } else {
                        cardTable.setFreezeType("1");
                        cardTable.setFreezeStatus(dto.getStatus());
                    }
                    cardTable.setType(dto.getType());
                    cardTable.setRemark(dto.getRemark());
                    cardTable.setTransactionTime(dto.getTransactionTime());
                    cardTable.setModifyTime(now);
                    cardRepository.save(cardTable);
                    CardOperateTable operateTable =
                            cardOperateRepository.findByMerchantOrderNo(dto.getMerchantOrderNo());
                    if (operateTable != null) {
                        operateTable.setStatus(dto.getStatus());
                        cardOperateRepository.save(operateTable);
                    }
                }
                break;
            case "withdraw"://扣款；
                CardWithdrawTable withdrawTable = withdrawRepository.findByMerchantOrderNo(dto.getMerchantOrderNo());
                if (withdrawTable == null) {
                    throw new ServiceException("交易不存在");
                }
                if (!withdrawTable.getStatus().equals(dto.getStatus())) {
                    BigDecimal amount = withdrawTable.getAmount();
                    String type = withdrawTable.getType();
                    BeanUtils.copyProperties(dto, withdrawTable);
                    withdrawTable.setModifyTime(now);
                    withdrawTable.setAmount(amount);
                    withdrawTable.setType(type);
                    withdrawRepository.save(withdrawTable);
                    CardOperateTable cardOperateTable =
                            cardOperateRepository.findByMerchantOrderNo(dto.getMerchantOrderNo());
                    if (cardOperateTable != null) {
                        cardOperateTable.setStatus(dto.getStatus());
                        cardOperateTable.setModifyTime(now);
                        cardOperateRepository.save(cardOperateTable);
                    }
                    if ("1".equals(withdrawTable.getType())) {
                        CardGlobalOrderTable globalOrderTable =
                                globalOrderRepository.findByDeductionMerchantOrderNo(dto.getMerchantOrderNo());
                        if (globalOrderTable != null) {
                            globalOrderTable.setDeductionAccountStatus(GlobalOrderDeductionTypeEnum.matchCode(dto.getStatus()));
                            globalOrderTable.setModifyTime(now);
                            globalOrderRepository.save(globalOrderTable);
                            if ("2".equals(globalOrderTable.getDeductionAccountStatus())) {
                                CardMerchantUserTable userTable =
                                        merchantUserRepository.getByUniqueId(globalOrderTable.getUniqueId());
                                // 将收款人信息保存到数据库
                                CardGlobalReceiverInfoTable receiverInfoTable =
                                        globalReceiverInfoRepository.findByUniqueIdAndReceiverBankAccountNumber(globalOrderTable.getUniqueId(), globalOrderTable.getReceiverBankAccountNumber());
                                if (receiverInfoTable == null) {
                                    receiverInfoTable = new CardGlobalReceiverInfoTable();
                                    BeanUtilPlus.copyPropertiesIgnoreNull(globalOrderTable, receiverInfoTable);
                                    receiverInfoTable.setId(null);
                                    receiverInfoTable.setAddTime(now);
                                } else {
                                    Long id = receiverInfoTable.getId();
                                    BeanUtilPlus.copyPropertiesIgnoreNull(globalOrderTable, receiverInfoTable);
                                    receiverInfoTable.setId(id);
                                    receiverInfoTable.setModifyTime(now);
                                }
                                globalReceiverInfoRepository.save(receiverInfoTable);
                                // 发邮件给审核人员
                                sendGlobalOrderReviewEmail(userTable, globalOrderTable);
                            }
                        }
                    } else if ("2".equals(withdrawTable.getType())) {
                        UcardPayCallbackEntity callbackEntity = new UcardPayCallbackEntity();
                        callbackEntity.setOrderNo(withdrawTable.getDepositOrderNo());
                        callbackEntity.setInternalOrderNo(withdrawTable.getInternalOrderNo());
                        callbackEntity.setAmount(withdrawTable.getAmount());
                        callbackEntity.setCallBackUrl(withdrawTable.getCallbackUrl());
                        if (Constants.SUCCESS.equals(dto.getStatus())) {
                            callbackEntity.setStatus(2);
                            callbackEntity.setApproveDesc("");
                        } else if (Constants.FAIL.equals(dto.getStatus())) {
                            callbackEntity.setStatus(3);
                            callbackEntity.setApproveDesc("103");
                        }
                        mqSendService.send(Constants.UCARD_DEPOSIT_CALLBACK, callbackEntity);
                    }
                }
                break;
            case "update_pin"://更新PIN
                UcardCardTable ucardCardTable = cardRepository.findByCardNo(dto.getCardNo());
                if (ucardCardTable == null) {
                    throw new ServiceException("卡不存在");
                }
                CardPasswordResetTable cardPasswordResetTable =
                        passwordResetRepository.findFirstByMerchantOrderNo(dto.getMerchantOrderNo());
                if (cardPasswordResetTable == null) {
                    throw new ServiceException("卡密码修改记录不存在");
                }
                if (!cardPasswordResetTable.getStatus().equals(dto.getStatus())) {
                    cardPasswordResetTable.setStatus(dto.getStatus());
                    cardPasswordResetTable.setModifyTime(now);
                    passwordResetRepository.save(cardPasswordResetTable);
                    CardOperateTable operateTable =
                            cardOperateRepository.findByMerchantOrderNo(dto.getMerchantOrderNo());
                    if (operateTable != null) {
                        operateTable.setStatus(dto.getStatus());
                        operateTable.setModifyTime(now);
                        cardOperateRepository.save(operateTable);
                    }
                    if (Constants.SUCCESS.equals(dto.getStatus())) {
                        ucardCardTable.setPin(cardPasswordResetTable.getPassword());
                        ucardCardTable.setType(dto.getType());
                        ucardCardTable.setModifyTime(now);
                        cardRepository.save(ucardCardTable);
                        // 修改密码成功给客户发邮件
                        CardMerchantUserTable cardMerchantUserTable =
                                merchantUserRepository.getByUniqueId(ucardCardTable.getUniqueId());
                        sendCardUpdatePasswordSuccessEmail(cardMerchantUserTable, ucardCardTable.getCardNumber());
                    }
                }
                break;
            case "blocked"://锁卡
                cardTable = cardRepository.findByCardNo(dto.getCardNo());
                if (cardTable == null) {
                    throw new ServiceException("卡不存在");
                }
                if (Constants.SUCCESS.equals(dto.getStatus())) {
                    cardTable.setBlocked(true);
                    cardTable.setType(dto.getType());
                    cardTable.setModifyTime(now);
                    cardRepository.save(cardTable);
                }
                break;
            default:
                break;
        }
    }

    /**
     * 卡授权交易通知
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cardAuthTransactionNotify(WasabiAuthTransactionDto dto) throws ServiceException {
        // 查询数据库是否存在该条数据记录 存在则更新 不存在则新增
        CardTransacOrderTable table = cardTransacOrderRepository.findByCardNoAndTradeNo(dto.getCardNo(),
                dto.getTradeNo());
        if (table == null) {
            table = new CardTransacOrderTable();
            table.setAddTime(new Date());
        } else {
            table.setModifyTime(new Date());
        }
        BeanUtils.copyProperties(dto, table);
        cardTransacOrderRepository.save(table);
    }

    /**
     * 卡授权交易冲正通知
     */
    @Override
    public void cardFeePatchNotify(WasabiFeePatchNotifyDto dto) throws ServiceException {
        CardAuthorizationFeeTable table = new CardAuthorizationFeeTable();
        BeanUtils.copyProperties(dto, table);
        table.setDeductionStatus("1");
//        authorizationFeeRepository.save(table);
    }

    /**
     * 卡3ds通知
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void card3dsNotify(WasabiThird3dsTransactionDto dto) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        UcardCardTable cardTable = cardRepository.findByCardNo(dto.getCardNo());
        if (cardTable == null) {
            throw new ServiceException("卡不存在");
        }
        CardMerchantUserTable userTable = merchantUserRepository.findByUniqueId(cardTable.getUniqueId());
        String values = RsaUtils.decryptPrivateKey(dto.getValues(), config.getPrivateKey());
        if (StringUtils.isEmpty(values)) {
            log.error("卡3ds通知-解密参数为空,{}", JSON.toJSONString(dto));
            throw new ServiceException("卡3ds通知-解密参数为空");
        }
        Card3dsTransactionTable table = new Card3dsTransactionTable();
        BeanUtils.copyProperties(dto, table);
        table.setValues(values);
        table.setAddTime(new Date());
        table.setTransactionTime(DateUtil.formatTimestamp(dto.getTransactionTime()));
        table.setExpirationTime(DateUtil.formatTimestamp(dto.getExpirationTime()));
        card3dsTransactionRepository.save(table);
        switch (dto.getType()) {
            case "activation_code":
                // 发邮件给用户
//                sendUserActivationCodeEmail(userTable, cardTable.getCardNumber(), values);
                break;
            case "third_3ds_otp":
                // 发邮件给用户
                sendUser3dsEmail(userTable, cardTable.getCardNumber(), values, dto);
                break;
            case "auth_url":
                // 发邮件给用户
                sendUserAuthUrlEmail(userTable, cardTable.getCardNumber(), values, dto);
                break;
            default:
                break;
        }
    }

    /**
     * 持卡人通知
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cardHolderNotify(WasabiHolderQueryDto dto) throws ServiceException {
        try {
            CardApplyTable applyTable =
                    cardApplyRepository.findFirstByHolderMerchantOrderNoOrderByAddTimeDesc(dto.getMerchantOrderNo());
            if (null == applyTable) {
                throw new ServiceException("持卡人不存在");
            }
            if (!applyTable.getHolderStatus().equals(dto.getStatus())) {
                applyTable.setHolderId(dto.getHolderId());
                applyTable.setHolderStatus(dto.getStatus());
                applyTable.setHolderDescription(dto.getDescription());
                cardApplyRepository.save(applyTable);
                mqSendService.send(Constants.WASABICARD_CARD_APPLY_PROGRESS, JSON.toJSONString(applyTable),
                        3 * 60 * 1000);
            }

        } catch (Exception e) {
            log.error("持卡人通知回调失败", e);
            throw new ServiceException("持卡人通知回调失败");
        }
    }

    /**
     * 实体卡事件通知(激活)
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void physicalCardNotify(WasabiPhysicalCardNotifyDto dto) throws ServiceException {
        try {
            UcardCardTable table = cardRepository.findByCardNo(dto.getCardNo());
            if (table == null) {
                throw new ServiceException("卡片不存在");
            }
            if (Constants.SUCCESS.equals(table.getStatus())) {
                throw new ServiceException("卡片已激活");
            }
            if (!table.getStatus().equals(dto.getStatus())) {
                if (Constants.SUCCESS.equals(dto.getStatus())) {
                    mqSendService.send(Constants.WASABICARD_CARD_INFO_REFRESH, JSONObject.toJSONString(table),
                            60 * 1000);
                    return;
                }
                CardActivateTable cardActivateTable =
                        cardActivateRepository.findByMerchantOrderNo(dto.getMerchantOrderNo());
                cardActivateTable.setStatus(dto.getStatus());
                cardActivateTable.setRemark(dto.getRemark());
                cardActivateTable.setDescription(dto.getDescription());
                cardActivateTable.setModifyTime(new Date());
                cardActivateRepository.save(cardActivateTable);
            }
        } catch (Exception e) {
            log.error("卡片激活结果通知回调失败", e);
            throw new ServiceException("卡片激活结果通知回调失败");
        }
    }

    /**
     * 速汇交易通知
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void expressRemittanceTransactionNotify(WasabiTransactionTransferDto dto) throws ServiceException {
        try {
            CardGlobalOrderTable table = globalOrderRepository.findByOrderNo(dto.getOrderNo());
            if (table == null) {
                throw new ServiceException("订单不存在: {}", dto.getOrderNo());
            }
            CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(table.getUniqueId());
            // 订单状态为成功 直接返回
            if (table.getStatus().equals(Constants.SUCCESS)) {
                return;
            }
            boolean needSave = false;
            boolean needSendEmail = false;
            if (dto.getComplianceStatus().equals(GlobalComplianceStatusEnum.PENDING_CHECK.getStatus())) {
                needSendEmail = true;
            }
            if (!table.getStatus().equals(dto.getStatus())) {
                table.setStatus(dto.getStatus());
                needSave = true;
            }
            if (!table.getComplianceStatus().equals(dto.getComplianceStatus())) {
                table.setComplianceStatus(dto.getComplianceStatus());
                needSave = true;
            }
            if (needSave) {
                globalOrderRepository.save(table);
            }
            // 如果存在需要补充的资料保存到数据库中
            if (CollectionUtils.isNotEmpty(dto.getRfiInfos())) {
                for (WasabiRfiInfosDto rfiInfosDto : dto.getRfiInfos()) {
                    if (StringUtils.isNotEmpty(rfiInfosDto.getStatus()) && rfiInfosDto.getStatus().equals(GlobalComplianceStatusEnum.PENDING_CHECK.getStatus())) {
                        needSendEmail = true;
                    }
                    CardGlobalOrderDataTable orderDataTable =
                            globalOrderDataRepository.findByOrderNoAndRfiId(rfiInfosDto.getOrderNo(),
                                    rfiInfosDto.getRfiId());
                    if (orderDataTable == null) {
                        orderDataTable = new CardGlobalOrderDataTable();
                    }
                    BeanUtils.copyProperties(rfiInfosDto, orderDataTable);
                    WasabiRfiFieldInfoDto rfiFieldInfo = rfiInfosDto.getRfiFieldInfo();
                    if (rfiFieldInfo != null) {
                        BeanUtils.copyProperties(rfiFieldInfo, orderDataTable);
                        List<WasabiMethodDto> selectValues = rfiFieldInfo.getSelectValues();
                        if (CollectionUtils.isNotEmpty(selectValues)) {
                            globalOrderDataAvailableRepository.deleteAllByOrderNoAndRfiId(rfiInfosDto.getOrderNo(),
                                    rfiInfosDto.getRfiId());
                            for (WasabiMethodDto selectValue : selectValues) {
                                CardGlobalOrderDataAvailableTable availableTable =
                                        new CardGlobalOrderDataAvailableTable();
                                availableTable.setOrderNo(rfiInfosDto.getOrderNo());
                                availableTable.setRfiId(rfiInfosDto.getRfiId());
                                availableTable.setValueId(selectValue.getValueId());
                                availableTable.setValue(selectValue.getValue());
                                globalOrderDataAvailableRepository.save(availableTable);
                            }
                        }
                    }
                    globalOrderDataRepository.save(orderDataTable);
                }
            }
            if (needSendEmail) {
                // 补充资料邮件
                sendGlobalOrderSupplementaryDataEmail(userTable, table);
            }

            if (dto.getStatus().equals(Constants.SUCCESS)) {
                // 发送邮件通知用户
                sendGlobalOrderTransactionSuccessEmail(userTable, table);
            } else if (dto.getStatus().equals(Constants.FAIL)) {
                // 订单失败发送邮件通知用户
                sendGlobalOrderTransactionFailEmail(userTable, table);
                // 退费
                refundTransacOrder(table);
            }
        } catch (Exception e) {
            log.error("速汇交易通知回调失败", e);
            throw new ServiceException("速汇交易通知回调失败");
        }
    }

    /**
     * 激活中数据查询卡信息并更新数据库
     */
    @JmsListener(destination = Constants.WASABICARD_CARD_INFO_REFRESH)
    @Transactional(rollbackFor = Exception.class)
    public void activateCallback(String content) throws Exception {

        log.info(String.format("WASABICARD_CARD_ACTIVATE_QUERY,content:%s", content));
        UcardCardTable table = JSON.parseObject(content, UcardCardTable.class);
        if (Constants.SUCCESS.equals(table.getStatus())) {
            return;
        }

        WasabiCardInfoEntity infoEntity = new WasabiCardInfoEntity();
        infoEntity.setCardNo(table.getCardNo());

        WasabiCardInfoDto cardInfo = getCardInfo(infoEntity);
        if (cardInfo == null) {
            return;
        }
        if ("Normal".equals(cardInfo.getStatus())) {
            WasabiBalanceInfoEntity balanceInfoEntity = new WasabiBalanceInfoEntity();
            balanceInfoEntity.setCardNo(cardInfo.getCardNo());
            WasabiCardSensitiveDto cardSensitiveDto = getCardSensitive(balanceInfoEntity);
            String cvv = cardSensitiveDto.getCvv();
            String expireDate = cardSensitiveDto.getExpireDate();
            String cardNumber = cardSensitiveDto.getCardNumber();
            String activateUrl = cardSensitiveDto.getActivateUrl();
            table.setCardNumber(cardNumber);
            table.setActivateUrl(activateUrl);
            table.setCvv(cvv);
            table.setExpireDate(expireDate);
            table.setBlocked(cardInfo.getBlocked());
            table.setRemark(cardInfo.getRemark());
            table.setModifyTime(new Date());
            cardRepository.save(table);
            if (StringUtils.isEmpty(cvv)) {
                mqSendService.send(Constants.WASABICARD_CARD_INFO_REFRESH, JSONObject.toJSONString(table));
                return;
            }
            CardActivateTable cardActivateTable =
                    cardActivateRepository.findByMerchantOrderNo(table.getMerchantOrderNo());
            if (cardActivateTable != null && !Constants.SUCCESS.equals(cardActivateTable.getStatus())) {
                cardActivateTable.setStatus(Constants.SUCCESS);
                cardActivateTable.setModifyTime(new Date());
                cardActivateRepository.save(cardActivateTable);
            }
            // 查询是否存在充值默认金额订单
            CardRechargeOrderTable rechargeOrderTable =
                    rechargeOrderRepository.getFirstByCardNoAndRechargeType(table.getCardNo(), "1");
            if (rechargeOrderTable == null) {
                SysConfigTable sysConfigTable = sysConfigService.getByCode(Constants.WASABI_CARD_RECHARGE_AMOUNT);
                if (sysConfigTable == null) {
                    throw ServiceException.exception(Constants.SYSTEM_ERROR);
                }
                // 充值默认金额
                WasabiConfig config = getWasabiConfig();
                WasabiBalanceEntity depositEntity = new WasabiBalanceEntity();
                depositEntity.setCardNo(table.getCardNo());
                depositEntity.setMerchantOrderNo(ObjectIdUtil.genSnowFlakeIdStr(config.getMerchantTag()));
                depositEntity.setAmount(new BigDecimal(sysConfigTable.getValue()));
                if (depositEntity.getAmount().compareTo(BigDecimal.ZERO) <= 0) {

                    table.setStatus(Constants.SUCCESS);
                    table.setModifyTime(new Date());
                    cardRepository.save(table);
                    sendActivateCardSuccessEmail(table);
                    return;
                }

                TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getCardDepositUrl(),
                        JSON.toJSONString(depositEntity), config);
                verifySignature(result, config.getWsbPublicKey());
                ResultData<WasabiDepositDto> resultData = JSON.parseObject(result.getSecond(),
                        new TypeReference<ResultData<WasabiDepositDto>>() {
                        }
                );
                if (!resultData.isSuccess()) {
                    log.error("WASABICARD_CARD_ACTIVATE_QUERY ：充值默认金额异常！");
                    return;
                }
                WasabiDepositDto depositDto = resultData.getData();

                CardRechargeOrderTable orderTable = new CardRechargeOrderTable();
                BeanUtils.copyProperties(depositDto, orderTable);
                orderTable.setRechargeFee(BigDecimal.ZERO);
                orderTable.setApproveStatus(2);
                orderTable.setUniqueId(table.getUniqueId());
                orderTable.setTransactionTime(DateUtil.formatTimestamp(depositDto.getTransactionTime()));
                orderTable.setCurrency("USD");
                orderTable.setReceivedCurrency("USD");
                orderTable.setAddTime(new Date());
                orderTable.setRechargeType("1");
                orderTable.setCardNumber(table.getCardNumber());
                rechargeOrderRepository.save(orderTable);

                CardOperateTable cardOperateTable = new CardOperateTable();
                cardOperateTable.setCardNo(table.getCardNo());
                cardOperateTable.setOperateUser(null);
                cardOperateTable.setOperateType(9);
                cardOperateTable.setStatus(Constants.SUCCESS);
                cardOperateTable.setAddTime(new Date());
                cardOperateTable.setUniqueId(table.getUniqueId());
                cardOperateRepository.save(cardOperateTable);

                if (Constants.SUCCESS.equals(orderTable.getStatus())) {
                    table.setStatus(Constants.SUCCESS);
                    table.setModifyTime(new Date());
                    cardRepository.save(table);
                    // 激活成功发送邮件通知
                    sendActivateCardSuccessEmail(table);
                }
            }
        }
    }

    /**
     * 卡信息刷新
     */
    @Override
    public BaseResultDto refreshCardInfo(CardInfoRefreshEntity entity) throws ServiceException {
        UcardCardTable table = cardRepository.getFirstById(entity.getId());
        if (table == null) {
            return BaseResultDto.error("The card information does not exist");
        }
        if ("wait_process".equals(table.getStatus()) || "processing".equals(table.getStatus())) {
            mqSendService.send(Constants.WASABICARD_CARD_INFO_REFRESH, JSONObject.toJSONString(table));
            return BaseResultDto.success();
        }
        WasabiCardInfoEntity infoEntity = new WasabiCardInfoEntity();
        infoEntity.setCardNo(table.getCardNo());
        WasabiCardInfoDto cardInfo = getCardInfo(infoEntity);
        if (cardInfo == null) {
            return BaseResultDto.success();
        }
        String status = cardInfo.getStatus();
        if ("Normal".equals(status)) {
            if (StringUtils.isEmpty(table.getCvv())) {
                mqSendService.send(Constants.WASABICARD_CARD_INFO_REFRESH, JSONObject.toJSONString(table));
            }
        } else if ("Freeze".equals(status)) {
            if (!"2".equals(table.getFreezeType()) || !"success".equals(table.getFreezeStatus())) {
                table.setFreezeType("2");
                table.setFreezeStatus("success");
                cardRepository.save(table);
            }
        } else if ("Freezing".equals(status)) {
            if (!"2".equals(table.getFreezeType()) || !"processing".equals(table.getFreezeStatus())) {
                table.setFreezeType("2");
                table.setFreezeStatus("processing");
                cardRepository.save(table);
            }
        } else if ("UnFreezing".equals(status)) {
            if (!"1".equals(table.getFreezeType()) || !"processing".equals(table.getFreezeStatus())) {
                table.setFreezeType("1");
                table.setFreezeStatus("processing");
                cardRepository.save(table);
            }
        } else if ("cancel".equals(status)) {
            if (!Constants.SUCCESS.equals(table.getCancelStatus())) {
                table.setCancelStatus(Constants.SUCCESS);
                cardRepository.save(table);
            }
        } else if ("blocked".equals(status)) {
            if (!table.getBlocked()) {
                table.setBlocked(true);
                cardRepository.save(table);
            }
        }
        return BaseResultDto.success();
    }

    /**
     * 查询激活码
     */
    @Override
    public BaseResultDto<WasabiThird3dsTransactionDto> getActivationCode(WasabiThird3dsTransactionEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            entity.setPageNum(1L);
            entity.setPageSize(10L);
            entity.setType("activation_code");
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getThird3dsTransactionUrl(),
                            JSON.toJSONString(entity)
                            , config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData resultData = JSON.parseObject(result.getSecond(), ResultData.class);

            JSONObject data = JSON.parseObject(resultData.getData().toString());
            Integer total = data.getInteger("total");
            if (total == null || total <= 0) {
                return BaseResultDto.error("未查询到数据");
            }
            JSONArray records = data.getJSONArray("records");
            List<WasabiThird3dsTransactionDto> dtos = records.toJavaList(WasabiThird3dsTransactionDto.class);
            WasabiThird3dsTransactionDto dto = null;
            if (CollectionUtils.isNotEmpty(dtos)) {
                dto = dtos.get(0);
                if (StringUtils.isNotEmpty(dto.getValues())) {
                    dto.setValues(RsaUtils.decryptPrivateKey(dto.getValues(), config.getPrivateKey()));
                }
            }
            UcardCardTable cardTable = cardRepository.getByCardNo(entity.getCardNo());
            CardOperateTable operateTable = new CardOperateTable();
            operateTable.setCardNo(cardTable.getCardNo());
            operateTable.setOperateUser(entity.getCId());
            operateTable.setOperateType(7);
            operateTable.setStatus(Constants.SUCCESS);
            operateTable.setAddTime(new Date());
            operateTable.setUniqueId(cardTable.getUniqueId());
            cardOperateRepository.save(operateTable);
            return BaseResultDto.success(dto);
        } catch (Exception e) {
            log.error("查询激活码失败", e);
            throw new ServiceException("查询激活码失败");
        }
    }

    /**
     * 身份认证获取web链接
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto getWebsdkLink(WebsdkLinkAliEntity entity, Long id) throws ServiceException {

        CustomInfoTable table = customInfoRepository.findFirstById(id);
        if (table == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        CardMerchantUserTable merchantUserTable = merchantUserRepository.findFirstById(entity.getCardId());
        if (merchantUserTable == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        if (1 == merchantUserTable.getAuthStatus()) {
            return BaseResultDto.result(201, "已认证");
        }
        CardKycStatusTable kycStatusTable = kycStatusRepository.findFirstByUniqueId(merchantUserTable.getUniqueId());
        if (2 == kycStatusTable.getStatus()) {
            throw ServiceException.exception("已手动审核成功");
        }

        CustomKycApproveTable approveTable = customKycApproveRepository.findFirstByCustomIdAndStatus(table.getId(), 1);
        if (null != approveTable) {
            throw ServiceException.exception("正在审核中");
        }
        SysConfigTable sysConfigTable = sysConfigService.getByCode(ConfigConstants.CUSTOM_VERIFY_KYC_PROPERTY);
        if (sysConfigTable == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        SysKycConfigTable kycConfig = sysKycConfigService.getByCodeAndCountryAndEnable(sysConfigTable.getValue(),
                table.getCountry(), 1);
        if (kycConfig == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }

        if (2 == kycConfig.getKycType() && !Strings.isNullOrEmpty(merchantUserTable.getKycVerifyUrl())) {
            return BaseResultDto.success(Constants.SUCCESS, merchantUserTable.getKycVerifyUrl());
        }
        String cardType = table.getCardType();
        if ("CN".equals(merchantUserTable.getCountry()) && "PASSPORT".equals(merchantUserTable.getIdType())) {
            cardType = "PASSPORT";
        }

        CustomKycVerifyRecodeEntity customKycVerifyRecodeEntity = new CustomKycVerifyRecodeEntity();
        customKycVerifyRecodeEntity.setCountry(merchantUserTable.getCountry());
        customKycVerifyRecodeEntity.setConfig(ConfigConstants.CUSTOM_VERIFY_KYC_PROPERTY);
        customKycVerifyRecodeEntity.setCardType(cardType);
        customKycVerifyRecodeEntity.setVerifyId(merchantUserTable.getId());
        customKycVerifyRecodeEntity.setVerifyType(6);
        customKycVerifyRecodeEntity.setCustomAuth(table.getAuthStatus());
        customKycVerifyRecodeEntity.setEndUserId(Strings.isNullOrEmpty(table.getEndUserId()) ?
                UUID.randomUUID().toString() : table.getEndUserId());
        customKycVerifyRecodeEntity.setCustomId(table.getId());

        /*if (3 != kycConfig.getKycType()) {*/
//        merchantUserTable.setExternalUserId(customKycVerifyRecodeEntity.getEndUserId());
        merchantUserTable.setExternalUserId(Strings.isNullOrEmpty(table.getExternalUserId()) ?
                UUID.randomUUID().toString() : table.getExternalUserId());
        customKycVerifyRecodeEntity.setExternalUserId(merchantUserTable.getExternalUserId());
            /*String applicantId = kycFeignService.createApplicant(customKycVerifyRecodeEntity);

            customKycVerifyRecodeEntity.setApplicantId(applicantId);
            List<CustomFileTable> fileList = customFileDao.findFileByType(table.getId());
            if(fileList != null && fileList.size() > 0){
                customKycVerifyRecodeEntity.setVerifyType(1);
                customKycVerifyRecodeEntity.setVerifyFile(fileList.stream().map(CustomFileTable::getPath).collect
        (Collectors.joining(",")));
                kycFeignService.uploadImage(customKycVerifyRecodeEntity);
            }*//*

            bankTable.setApplicantId(applicantId);
        }*/

//        merchantUserTable.setAuthStatus(0);
        //bankTable.setExternalUserId(Strings.isNullOrEmpty(customKycVerifyRecodeEntity.getExternalUserId()) ?
        // UUIDUtil.getUUID() : customKycVerifyRecodeEntity.getExternalUserId());

        customKycVerifyRecodeEntity.setMetaInfo(entity.getMetaInfo());
        //customKycVerifyRecodeEntity.setExternalUserId(bankTable.getExternalUserId());
        //customKycVerifyRecodeEntity.setApplicantId(bankTable.getApplicantId());
        log.info(String.format("customKycVerifyRecodeEntity is :%s", JSON.toJSONString(customKycVerifyRecodeEntity)));
        BaseResultDto<CustomKycVerifyRecodeEntity> dto = kycFeignService.getWebsdkLink(customKycVerifyRecodeEntity);
        CustomKycVerifyRecodeEntity recodeEntity = dto.getData();
        if (2 == kycConfig.getKycType() && Strings.isNullOrEmpty(merchantUserTable.getKycVerifyUrl())) {
            merchantUserTable.setKycVerifyUrl(recodeEntity.getWebUrl());
            merchantUserTable.setApplicantId(recodeEntity.getApplicantId());
        }
        merchantUserRepository.save(merchantUserTable);

        if (Strings.isNullOrEmpty(table.getEndUserId())) {
            table.setEndUserId(recodeEntity.getEndUserId());
        }
        if (Strings.isNullOrEmpty(table.getExternalUserId())) {
            table.setExternalUserId(recodeEntity.getExternalUserId());
        }
        customInfoRepository.save(table);
        if (Strings.isNullOrEmpty(kycStatusTable.getExternalUserId())) {
            kycStatusTable.setExternalUserId(table.getExternalUserId());
            kycStatusRepository.save(kycStatusTable);
        }

        CardOperateTable operateTable = new CardOperateTable();
        operateTable.setOperateUser(table.getCId());
        operateTable.setOperateType(3);
        operateTable.setStatus(Constants.SUCCESS);
        operateTable.setAddTime(new Date());
        operateTable.setUniqueId(merchantUserTable.getUniqueId());
        cardOperateRepository.save(operateTable);

        return BaseResultDto.success(Constants.SUCCESS, recodeEntity.getWebUrl());
    }

    /**
     * 新增权限规则
     */
    @Override
    public BaseResultDto addPermission(CardPermissionAddEntity entity) throws ServiceException {
        if (StringUtils.isEmpty(entity.getCIdRegexp()) && StringUtils.isEmpty(entity.getEmailRegexp())
                && StringUtils.isEmpty(entity.getPhoneRegexp()) && StringUtils.isEmpty(entity.getCountryCodes())
                && StringUtils.isEmpty(entity.getStateEnNames()) && StringUtils.isEmpty(entity.getCityEnNames())) {
            throw new ServiceException("权限规则不能全部为空");
        }
        // 校验 regexp 字段格式
        if (StringUtils.isNotEmpty(entity.getCIdRegexp()) && !isValidRegexpFormat(entity.getCIdRegexp())) {
            throw new ServiceException("CID正则表达式格式不正确");
        }
        if (StringUtils.isNotEmpty(entity.getEmailRegexp()) && !isValidRegexpFormat(entity.getEmailRegexp())) {
            throw new ServiceException("邮箱正则表达式格式不正确");
        }
        if (StringUtils.isNotEmpty(entity.getPhoneRegexp()) && !isValidRegexpFormat(entity.getPhoneRegexp())) {
            throw new ServiceException("手机号码正则表达式格式不正确");
        }
        CardPermissionTable table = new CardPermissionTable();
        BeanUtils.copyProperties(entity, table);
        permissionRepository.save(table);
        return BaseResultDto.success();
    }

    /**
     * 更新权限规则
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto updatePermission(CardPermissionUpdateEntity entity) throws ServiceException {
        CardPermissionTable table = permissionRepository.getFirstById(entity.getId());
        if (table == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        if (table.getEnableStatus().equals(entity.getEnableStatus())) {
            return BaseResultDto.success();
        }
        BeanUtils.copyProperties(entity, table);
        permissionRepository.save(table);
        return BaseResultDto.success();
    }

    /**
     * 删除权限规则
     */
    @Override
    public BaseResultDto deletePermission(CardPermissionDeleteEntity entity) throws ServiceException {
        permissionRepository.delete(entity.getId());
        return BaseResultDto.success();
    }

    /**
     * 权限分页列表
     */
    @Override
    public ResultWithPagerDto<CardPermissionDto> queryPermissionPageList(CardPermissionPageEntity entity) throws ServiceException {
        try {
            // 获取总数
            Integer count = permissionMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }
            List<CardPermissionDto> dtos = permissionMapper.pageList(entity);

            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("卡权限分页查询失败", e);
            throw new ServiceException("卡权限分页查询失败");
        }
    }

    /**
     * 权限校验
     */
    @Override
    public BaseResultDto permissionVerify(CustomInfoEntity entity) throws ServiceException {
        List<CardPermissionTable> tables = permissionRepository.findByEnableStatus(1);
        if (CollectionUtils.isEmpty(tables)) {
            return BaseResultDto.success(false);
        }
        for (CardPermissionTable table : tables) {
            if (matchData(table, entity)) {
                return BaseResultDto.success(true);
            }
        }
        return BaseResultDto.success(false);
    }

    /**
     * 操作记录分页列表
     */
    @Override
    public ResultWithPagerDto<CardOperateDto> queryOperatePageList(CardOperatePageEntity entity) throws ServiceException {
        try {
            /**
             * 格式化时间
             */
            if (entity.getEndDate() != null) {
                entity.setEndDate(DateUtil.operationDay(entity.getEndDate(), 1));
            }
            // 获取总数
            Integer count = operateMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }
            List<CardOperateDto> dtos = operateMapper.pageList(entity);

            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("操作记录分页查询失败", e);
            throw new ServiceException("操作记录分页查询失败");
        }
    }

    /**
     * kyc认证详情
     */
    @Override
    public BaseResultDto kycStatusSingle(SingleLongEntity entity) throws ServiceException {
        CardKycSingleStatusDto dto = kycStatusMapper.getKycApproveById(entity.getId());
        if (dto == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        dto.setInfoName(CustomInfoUtil.getNameByCountry(dto.getInfoFirstName(), dto.getInfoLastName(),
                dto.getMiddle(), dto.getCountry()));
        List<CustomFileDto> cardFiles = new ArrayList<>();
        if (StringUtils.isNotEmpty(dto.getIdBackUrl())) {
            CustomFileDto backDto = new CustomFileDto();
            backDto.setPath(dto.getIdBackUrl());
            cardFiles.add(backDto);
        }
        if (StringUtils.isNotEmpty(dto.getIdFrontUrl())) {
            CustomFileDto frontDto = new CustomFileDto();
            frontDto.setPath(dto.getIdFrontUrl());
            cardFiles.add(frontDto);
        }
        if (StringUtils.isNotEmpty(dto.getIdHoldUrl())) {
            CustomFileDto holdDto = new CustomFileDto();
            holdDto.setPath(dto.getIdHoldUrl());
            cardFiles.add(holdDto);
        }
        dto.setCardFiles(cardFiles);
        dto.setCardName(CustomInfoUtil.getNameByCountry(dto.getFirstName(), null, dto.getLastName(), dto.getCountry()));
        List<CustomFileDto> kycFiles = new ArrayList<>();
        // 查询认证文件，判断国家是否是中国，不是中国直接使用externalUserId查询，
        // 中国，因为kyc审核表的externalUserId每次都会变，导致存在多次记录的情况直接用externalUserId查询认证文件表会查询不到数据
        // 根据用户的id(bankId)按照时间倒序查询 所有审核通过的externalUserId
        // 批量查询所有文件信息 按照时间倒序匹配有文件信息的数据
        if (dto.getKycAuthStatus() != null && dto.getKycAuthStatus().equals(1)) {
            List<CustomKycImageTable> imagefiles = null;
            if ("CN".equals(dto.getMerchantCountry())) {
                List<CustomKycApproveTable> approveTables =
                        customKycApproveRepository.findByCIdAndStatusOrderByAddTimeDesc(dto.getCId(), 2);
                if (CollectionUtils.isNotEmpty(approveTables)) {
                    List<String> externalUserIds = approveTables.stream()
                            .map(CustomKycApproveTable::getExternalUserId)
                            .filter(Objects::nonNull)
                            .collect(Collectors.toList());

                    // 批量查询所有相关的图片
                    List<CustomKycImageTable> allImages =
                            customKycImageRepository.findAllByExternalUserIdIn(externalUserIds);

                    // 按 externalUserId 分组
                    Map<String, List<CustomKycImageTable>> imagesByExternalUserId = allImages.stream()
                            .collect(Collectors.groupingBy(CustomKycImageTable::getExternalUserId));

                    for (CustomKycApproveTable approveTable : approveTables) {
                        List<CustomKycImageTable> images = imagesByExternalUserId.get(approveTable.getExternalUserId());
                        if (CollectionUtils.isNotEmpty(images)) {
                            dto.setKycStatus(approveTable.getStatus());
                            dto.setKycIdNumber(approveTable.getKycIdNumber());
                            dto.setKycNumber(approveTable.getKycNumber());
                            dto.setKycName(approveTable.getKycName());
                            dto.setKycApproveId(approveTable.getId());
                            dto.setKycAuthStatus(approveTable.getStatus());
                            imagefiles = images;
                            break;
                        }
                    }
                }
            } else {
                imagefiles = customKycImageRepository.findAllByExternalUserId(dto.getExternalUserId());
            }
            if (CollectionUtils.isNotEmpty(imagefiles)) {
                for (CustomKycImageTable fileTable : imagefiles) {
                    CustomFileDto fileDto = new CustomFileDto();
                    fileDto.setPath(fileTable.getFileUrl());
                    fileDto.setType(fileTable.getType());
                    kycFiles.add(fileDto);
                }
                dto.setKycFiles(kycFiles);
            }
        }


        List<CustomFileDto> infoFiles = new ArrayList<>();
        List<CustomFileTable> fileTables = customFileRepository.getAllByCustomId(dto.getCustomId());
        for (CustomFileTable fileTable : fileTables) {
            CustomFileDto fileDto = new CustomFileDto();
            BeanUtils.copyProperties(fileTable, fileDto);
            infoFiles.add(fileDto);
        }
        dto.setInfoFiles(infoFiles);
        return BaseResultDto.success(dto);
    }

    /**
     * 重新充值默认开卡金额
     */
    @Override
    public BaseResultDto rechargeDefaultAmount(CardRechargeDefaultAmountEntity entity) throws Exception {
        UcardCardTable table = cardRepository.getFirstById(entity.getId());
        if (table == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        if (Constants.SUCCESS.equals(table.getStatus())) {
            throw ServiceException.exception("卡片已激活成功！");
        }
        SysConfigTable sysConfigTable = sysConfigService.getByCode(Constants.WASABI_CARD_RECHARGE_AMOUNT);
        if (sysConfigTable == null) {
            throw ServiceException.exception(Constants.SYSTEM_ERROR);
        }
        // 查询是否存在充值处理中和充值成功的数据
        CardRechargeOrderTable rechargeOrderTable =
                rechargeOrderRepository.getFirstByCardNoAndStatusAndRechargeType(table.getCardNo(), "processing", "1");
        if (rechargeOrderTable != null) {
            return BaseResultDto.error("卡片充值处理中！");
        }
        CardRechargeOrderTable cardRechargeOrderTable =
                rechargeOrderRepository.getFirstByCardNoAndStatusAndRechargeType(table.getCardNo(), "success", "1");
        if (cardRechargeOrderTable != null) {
            table.setStatus(Constants.SUCCESS);
            table.setModifyTime(new Date());
            cardRepository.save(table);
            // 激活成功发送邮件通知
            sendActivateCardSuccessEmail(table);
            return BaseResultDto.success("卡片已充值默认金额成功！");
        }

        // 充值默认金额
        WasabiConfig config = getWasabiConfig();
        WasabiBalanceEntity depositEntity = new WasabiBalanceEntity();
        depositEntity.setCardNo(table.getCardNo());
        depositEntity.setMerchantOrderNo(ObjectIdUtil.genSnowFlakeIdStr(config.getMerchantTag()));
        depositEntity.setAmount(new BigDecimal(sysConfigTable.getValue()));

        if (depositEntity.getAmount().compareTo(BigDecimal.ZERO) <= 0) {

            table.setStatus(Constants.SUCCESS);
            table.setModifyTime(new Date());
            cardRepository.save(table);
            sendActivateCardSuccessEmail(table);
            return BaseResultDto.success();
        }

        TwoTuple<String, String> result = WasabiCommonUtil.post(config.getApiUrl() + config.getCardDepositUrl(),
                JSON.toJSONString(depositEntity), config);
        verifySignature(result, config.getWsbPublicKey());
        ResultData<WasabiDepositDto> resultData = JSON.parseObject(result.getSecond(),
                new TypeReference<ResultData<WasabiDepositDto>>() {
                }
        );
        if (!resultData.isSuccess()) {
            log.error("WASABICARD_CARD_RECHARGE ：充值默认金额异常！");
            return BaseResultDto.error(resultData.getMsg());
        }
        WasabiDepositDto depositDto = resultData.getData();

        CardRechargeOrderTable orderTable = new CardRechargeOrderTable();
        BeanUtils.copyProperties(depositDto, orderTable);
        orderTable.setReceivedCurrency("USD");
        orderTable.setCurrency("USD");
        orderTable.setRechargeFee(BigDecimal.ZERO);
        orderTable.setApproveStatus(2);
        orderTable.setUniqueId(table.getUniqueId());
        orderTable.setTransactionTime(DateUtil.formatTimestamp(depositDto.getTransactionTime()));
        orderTable.setAddTime(new Date());
        orderTable.setRechargeType("1");
        orderTable.setCardNumber(table.getCardNumber());
        rechargeOrderRepository.save(orderTable);

        CardOperateTable cardOperateTable = new CardOperateTable();
        cardOperateTable.setCardNo(table.getCardNo());
        cardOperateTable.setOperateUser(null);
        cardOperateTable.setOperateType(9);
        cardOperateTable.setStatus(Constants.SUCCESS);
        cardOperateTable.setAddTime(new Date());
        cardOperateTable.setUniqueId(table.getUniqueId());
        cardOperateRepository.save(cardOperateTable);

        if (Constants.SUCCESS.equals(depositDto.getStatus())) {
            table.setStatus(Constants.SUCCESS);
            table.setModifyTime(new Date());
            cardRepository.save(table);
            // 激活成功发送邮件通知
            sendActivateCardSuccessEmail(table);
        }

        return BaseResultDto.success();
    }

    /**
     * 卡扣款分页列表
     */
    @Override
    public ResultWithPagerDto<CardWithdrawDto> queryCardWithdrawPage(CardWithdrawPageEntity entity) throws ServiceException {
        try {
            // 获取总数
            Integer count = withdrawMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }
            List<CardWithdrawDto> dtos = withdrawMapper.pageList(entity);

            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("卡扣款分页列表查询失败", e);
            throw new ServiceException("卡扣款分页列表查询失败");
        }
    }

    /**
     * 卡3ds分页列表
     */
    @Override
    public ResultWithPagerDto<Card3dsTransactionDto> queryCard3dsTransactionPage(Card3dsTransactionPageEntity entity) throws ServiceException {
        try {
            // 获取总数
            Integer count = card3dsTransactionMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }
            List<Card3dsTransactionDto> dtos = card3dsTransactionMapper.pageList(entity);

            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("卡3ds分页列表查询失败", e);
            throw new ServiceException("卡3ds分页列表查询失败");
        }
    }

    /**
     * 校验实体卡卡号是否使用过
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto cardNumberVerify(CardNumberVerifyEntity entity) throws ServiceException {
        CardApplyTable table = cardApplyMapper.cardNumberVerify(entity.getCardNumber().trim());
        if (table != null) {
            saveCardNumberStatus(table.getCardNumber(), 3);
            return BaseResultDto.error("该实体卡卡号已被使用。");
        }
        saveCardNumberStatus(entity.getCardNumber(), 2);
        return BaseResultDto.success();
    }

    /**
     * 手动上传证件照
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto uploadCardPhotoManually(CardPhotoUploadManuallyEntity entity) throws ServiceException {
        CardMerchantUserTable table = merchantUserRepository.findFirstById(entity.getId());
        if (table == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        if (table.getPhotoStatus() != null && table.getPhotoStatus() == 1) {
            table.setPhotoStatus(null);
        } else {
            table.setPhotoStatus(1);
        }
        merchantUserRepository.save(table);

        CardOperateTable operateTable = new CardOperateTable();
        operateTable.setOperateUser(entity.getCId());
        operateTable.setOperateType(17);
        operateTable.setStatus(Constants.SUCCESS);
        operateTable.setAddTime(new Date());
        operateTable.setUniqueId(table.getUniqueId());
        cardOperateRepository.save(operateTable);
        // 发邮件给客户上传证件照片
        if (table.getPhotoStatus() != null && table.getPhotoStatus() == 1) {
            sendUserUploadPhotoEmail(table);
        }
        return BaseResultDto.success();
    }

    /**
     * 配置用户汇率
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto cardUserRateConfig(CardUserRateConfigEntity entity) throws ServiceException {
        if (entity.getCardFee() != null) {
            if (entity.getCardFee().compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("开卡费用不能小于0");
            }
            validateDecimalPlaces(entity.getCardFee(), "userCardFee", 2);
        }
        if (entity.getCardRechargeFeeRate() != null) {
            if (entity.getCardRechargeFeeRate().compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("卡充值费率不能小于0");
            }
            validateDecimalPlaces(entity.getCardRechargeFeeRate(), "userCardRechargeFeeRate", 2);
        }
        if (entity.getGlobalFeeRate() != null) {
            if (entity.getGlobalFeeRate().compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("速汇手续费率不能小于0");
            }
            validateDecimalPlaces(entity.getGlobalFeeRate(), "userGlobalFeeRate", 10);
        }
        if (entity.getGlobalFixedFee() != null) {
            if (entity.getGlobalFixedFee().compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("速汇固定费率不能小于0");
            }
            validateDecimalPlaces(entity.getGlobalFixedFee(), "userGlobalFixedFee", 10);
        }

        CardMerchantUserTable userTable = merchantUserRepository.findFirstById(entity.getId());
        if (userTable == null) {
            return BaseResultDto.error("用户不存在！");
        }

        BeanUtils.copyProperties(entity, userTable);
        merchantUserRepository.save(userTable);
        return BaseResultDto.success();
    }

    /**
     * 扫码上传文件
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto scanUpload(CardWebSocketEntity entity) throws ServiceException {
        pushMessage(entity);
        return BaseResultDto.success();
    }

    /**
     * 用户信息导出
     */
    @Override
    public BaseResultDto<List<MerchantUserDto>> merchantUserListExport(MerchantUserPageEntity entity) throws ServiceException {
        entity.setPage(null);
        List<MerchantUserDto> dtos = cardMerchantUserMapper.pageList(entity);
        return BaseResultDto.success(dtos);
    }

    /**
     * kyc 认证列表导出
     */
    @Override
    public BaseResultDto<List<CardKycStatusDto>> merchantKycListExport(CardKycStatusPageEntity entity) throws ServiceException {
        entity.setPage(null);
        List<CardKycStatusDto> dtos = kycStatusMapper.pageList(entity);
        return BaseResultDto.success(dtos);
    }

    /**
     * 卡片申请列表导出
     */
    @Override
    public BaseResultDto<List<CardApplyDto>> cardApplyListExport(CardApplyPageEntity entity) throws ServiceException {
        entity.setPage(null);
        /**
         * 格式化时间
         */
        if (entity.getEndDate() != null) {
            entity.setEndDate(DateUtil.operationDay(entity.getEndDate(), 1));
        }

        List<CardApplyDto> cardlist = cardApplyMapper.pageList(entity);

        return BaseResultDto.success(cardlist);
    }

    /**
     * 卡片列表导出
     */
    @Override
    public BaseResultDto<List<UcardCardDto>> cardListExport(CardPageEntity entity) throws ServiceException {
        entity.setPage(null);
        List<UcardCardDto> cardlist = cardMapper.pageList(entity);
        return BaseResultDto.success(cardlist);
    }

    /**
     * 交易记录列表导出
     */
    @Override
    public BaseResultDto<List<CardTransacOrderDto>> cardTransacRecordExport(CardTransacOrderPageEntity entity) throws ServiceException {
        entity.setPage(null);
        List<CardTransacOrderDto> dtos = cardTransacOrderMapper.pageList(entity);
        return BaseResultDto.success(dtos);
    }

    /**
     * 充值记录导出
     */
    @Override
    public BaseResultDto<List<CardRechargeOrderDto>> cardRechargeListExport(CardRechargePageEntity entity) throws ServiceException {
        entity.setPage(null);
        /**
         * 格式化时间
         */
        if (entity.getEndDate() != null) {
            entity.setEndDate(DateUtil.operationDay(entity.getEndDate(), 1));
        }
        List<CardRechargeOrderDto> dtos = rechargeOrderMapper.pageList(entity);
        return BaseResultDto.success(dtos);
    }

    /**
     * 钱包余额记录列表导出
     */
    @Override
    public BaseResultDto<List<CardWalletRecordDto>> cardWalletRecordExport(CardWalletRecordPageEntity entity) throws ServiceException {
        entity.setPage(null);
        /**
         * 格式化时间
         */
        if (entity.getEndDate() != null) {
            entity.setEndDate(DateUtil.operationDay(entity.getEndDate(), 1));
        }
        List<CardWalletRecordDto> dtos = walletRecordMapper.pageList(entity);
        return BaseResultDto.success(dtos);
    }

    /**
     * 操作记录列表导出
     */
    @Override
    public BaseResultDto<List<CardOperateDto>> cardOperateRecordExport(CardOperatePageEntity entity) throws ServiceException {
        entity.setPage(null);
        /**
         * 格式化时间
         */
        if (entity.getEndDate() != null) {
            entity.setEndDate(DateUtil.operationDay(entity.getEndDate(), 1));
        }
        List<CardOperateDto> dtos = operateMapper.pageList(entity);
        return BaseResultDto.success(dtos);
    }

    /**
     * 速汇订单导出
     */
    @Override
    public BaseResultDto<List<GlobalOrderDto>> globalOrderListExport(GlobalOrderPageEntity entity) throws ServiceException {
        entity.setPage(null);
        /**
         * 格式化时间
         */
        if (entity.getEndDate() != null) {
            entity.setEndDate(DateUtil.operationDay(entity.getEndDate(), 1));
        }
        List<GlobalOrderDto> dtos = globalOrderMapper.pageList(entity);
        if (CollectionUtils.isNotEmpty(dtos)) {
            dtos.forEach(dto -> {
                dto.setPayoutMethod(GlobalPayoutMethodEnum.matchRemark(dto.getPayoutMethod()));
                dto.setTransferType(GlobalTransferTypeEnum.matchType(dto.getTransferType()));
            });
        }
        return BaseResultDto.success(dtos);
    }

    /**
     * 3ds列表导出
     */
    @Override
    public BaseResultDto<List<Card3dsTransactionDto>> card3dsTransactionListExport(Card3dsTransactionPageEntity entity) throws ServiceException {
        entity.setPage(null);
        List<Card3dsTransactionDto> dtos = card3dsTransactionMapper.pageList(entity);
        return BaseResultDto.success(dtos);
    }

    /**
     * 卡扣款列表导出
     */
    @Override
    public BaseResultDto<List<CardWithdrawDto>> cardWithdrawListExport(CardWithdrawPageEntity entity) throws ServiceException {
        entity.setPage(null);
        List<CardWithdrawDto> dtos = withdrawMapper.pageList(entity);
        return BaseResultDto.success(dtos);
    }

    /**
     * 修改卡类型配置
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto updateCardTypeConfig(CardTypeConfigUpdateEntity entity) throws ServiceException {
        CardTypeTable typeTable = cardTypeRepository.findByCardTypeId(entity.getCardTypeId());
        if (typeTable == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        if (entity.getCardFee() != null) {
            if (entity.getCardFee().compareTo(BigDecimal.ZERO) < 0) {
                throw ServiceException.exception("卡片费用不能小于0");
            }
            validateDecimalPlaces(entity.getCardFee(), "cardFee", 2);
        }
        if (entity.getOriginalCardFee() != null) {
            if (entity.getOriginalCardFee().compareTo(BigDecimal.ZERO) < 0) {
                throw ServiceException.exception("原卡片费用不能小于0");
            }
            validateDecimalPlaces(entity.getOriginalCardFee(), "originalCardFee", 2);
        }
        if (entity.getRechargeMinQuota() != null) {
            // 校验充值最小额度配置
            if (entity.getRechargeMinQuota().compareTo(typeTable.getRechargeMinQuota()) < 0) {
                throw ServiceException.exception("充值最小额度不能小于三方充值的最小额度");
            }
            if (entity.getRechargeMinQuota().compareTo(typeTable.getRechargeMaxQuota()) > 0) {
                throw ServiceException.exception("充值最小额度不能大于三方充值的最大额度");
            }
            validateDecimalPlaces(entity.getRechargeMinQuota(), "rechargeMinQuota", 2);
        }
        if (entity.getRechargeMaxQuota() != null) {
            // 校验充值最大额度配置
            if (entity.getRechargeMaxQuota().compareTo(typeTable.getRechargeMaxQuota()) > 0) {
                throw ServiceException.exception("充值最大额度不能大于三方充值的最大额度");
            }
            if (entity.getRechargeMaxQuota().compareTo(typeTable.getRechargeMinQuota()) < 0) {
                throw ServiceException.exception("充值最大额度不能小于三方充值的最小额度");
            }
            validateDecimalPlaces(entity.getRechargeMinQuota(), "rechargeMinQuota", 2);
        }
        if (entity.getRechargeMaxQuota() != null && entity.getRechargeMinQuota() != null) {
            if (entity.getRechargeMaxQuota().compareTo(entity.getRechargeMinQuota()) < 0) {
                throw ServiceException.exception("充值最大额度不能小于充值最小额度");
            }
        }
        CardTypeConfigTable table = cardTypeConfigRepository.findByCardTypeId(entity.getCardTypeId());
        if (table == null) {
            table = new CardTypeConfigTable();
            table.setAddTime(new Date());
        } else {
            table.setModifyTime(new Date());
        }
        BeanUtils.copyProperties(entity, table);
        table.setIsContact(true);
        cardTypeConfigRepository.save(table);
        return BaseResultDto.success();
    }

    /**
     * 证件类型配置列表
     */
    @Override
    public ResultWithPagerDto<CardIdTypeConfigDto> getCardIdTypeConfigList(CardIdTypeConfigPageEntity entity) throws ServiceException {
        try {
            // 获取总数
            Integer count = cardIdTypeConfigMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }
            List<CardIdTypeConfigDto> dtos = cardIdTypeConfigMapper.pageList(entity);
            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("获取证件类型配置列表失败", e);
            throw new ServiceException("获取证件类型配置列表失败");
        }
    }

    /**
     * 批量更新证件类型配置
     */
    @Override
    public BaseResultDto batchAddCardIdTypeConfig(List<CardIdTypeConfigAddEntity> entitys) throws ServiceException {
        cardIdTypeConfigMapper.batchSave(entitys);
        return BaseResultDto.success();
    }

    /**
     * 更新证件类型配置
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto updateCardIdTypeConfig(CardIdTypeConfigUpdateEntity entity) throws ServiceException {
        CardIdTypeConfigTable table = cardIdTypeConfigRepository.findFirstById(entity.getId());
        if (table == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        BeanUtils.copyProperties(entity, table);
        cardIdTypeConfigRepository.save(table);
        return BaseResultDto.success();
    }

    /**
     * 批量删除证件类型配置
     */
    @Override
    public BaseResultDto deleteCardIdTypeConfig(BaseDeleteEntities entity) throws ServiceException {
        List<CardIdTypeConfigTable> tables = Lists.newArrayList();
        for (Long id : entity.getIds()) {
            CardIdTypeConfigTable table = new CardIdTypeConfigTable();
            table.setId(id);
            tables.add(table);
        }
        cardIdTypeConfigRepository.deleteInBatch(tables);
        return BaseResultDto.success();
    }

    /**
     * 校验谷歌验证码查询cvv码
     */
    @Override
    public BaseResultDto cardQueryCvv(CardVerifyGoogleCodeEntity entity) throws ServiceException {
        validateGoogleAuthenticatorCode(entity);
        UcardCardTable table = cardRepository.findOne(entity.getId());
        if (table == null) {
            return BaseResultDto.error(Constants.INFO_NOT_FOUND);
        }
        return BaseResultDto.success(Constants.SUCCESS, table.getCvv());
    }

    /**
     * 根据环境校验
     */
    @Override
    public void validateGoogleAuthenticatorCode(CardVerifyGoogleCodeEntity entity) throws ServiceException {
        for (int i = 0; i < environment.getActiveProfiles().length; i++) {
            if (environment.getActiveProfiles()[i].equals("prod")) {
                if (entity.getVerified() == null || !entity.getVerified().equals(1)) {
                    throw ServiceException.exception(Constants.GOOGLE_SECRET_NOT_BIND_ERROR);
                }
                if (Strings.isNullOrEmpty(entity.getSecretKey())) {
                    throw ServiceException.exception(Constants.GOOGLE_SECRET_NOT_EXIST_ERROR);
                }

                if (entity.getGaCode() == null || !GoogleAuthenticatorUtil.verifyCode(entity.getSecretKey(),
                        entity.getGaCode())) {
                    throw ServiceException.exception(Constants.GOOGLE_CAPTCHA_ERROR);
                }
                break;
            }
        }
    }

    /**
     * 根据卡号查询卡信息
     */
    @Override
    public BaseResultDto queryCardInfoByCardNumber(CardInfoQueryEntity entity) throws ServiceException {
        CardInfoQueryDto dto = cardMapper.cardInfoQuery(entity);
        return BaseResultDto.success(dto);
    }

    /**
     * 导入卡号
     */
    @Override
    public BaseResultDto cardNumberImport(List<CardNumberAddEntity> entityList) throws ServiceException {
        if (CollectionUtils.isEmpty(entityList)) {
            return BaseResultDto.success();
        }
        // 查询数据库中所有的卡号
        List<UcardCardNumberTable> tables = cardNumberRepository.findAll();
        // 查询使用过的卡号
        List<String> numberList = cardApplyMapper.queryAllUsedCardNumber();
        List<UcardCardNumberTable> tableList = Lists.newArrayList();
        // 过滤数据库中的卡号
        if (CollectionUtils.isNotEmpty(tables)) {
            List<String> cardNumbers =
                    tables.stream().map(UcardCardNumberTable::getCardNumber).collect(Collectors.toList());
            entityList =
                    entityList.stream().filter(entity -> !cardNumbers.contains(entity.getCardNumber())).collect(Collectors.toList());
        }
        // 过滤使用过的卡号
        if (CollectionUtils.isNotEmpty(numberList)) {
            entityList =
                    entityList.stream().filter(entity -> !numberList.contains(entity.getCardNumber())).collect(Collectors.toList());
        }

        entityList.forEach(entity -> {
            UcardCardNumberTable table = new UcardCardNumberTable();
            table.setCardNumber(entity.getCardNumber());
            table.setStatus(1);
            tableList.add(table);
        });
        if (CollectionUtils.isNotEmpty(tableList)) {
            List<List<UcardCardNumberTable>> lists = ListSplitUtil.split(tableList);
            for (List<UcardCardNumberTable> list : lists) {
                cardNumberMapper.batchSave(list);
            }
        }
        return BaseResultDto.success();
    }

    /**
     * 卡号列表
     */
    @Override
    public ResultWithPagerDto<CardNumberDto> queryCardNumberList(CardNumberPageEntity entity) throws ServiceException {
        try {
            // 获取总数
            Integer count = cardNumberMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }
            List<CardNumberDto> dtos = cardNumberMapper.pageList(entity);
            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("获取卡号列表失败", e);
            throw new ServiceException("获取卡号列表失败");
        }
    }

    /**
     * 卡号下拉列表
     */
    @Override
    public BaseResultDto<CardNumberDto> queryCardNumberDropdown(CardNumberPageEntity entity) throws ServiceException {
        entity.setPage(null);
        List<CardNumberDto> dtos = cardNumberMapper.pageList(entity);
        return BaseResultDto.success(dtos);
    }

    /**
     * 修改卡号状态
     */
    @Override
    public BaseResultDto saveCardNumberStatus(CardNumberUpdateEntity entity) throws ServiceException {
        UcardCardNumberTable table = cardNumberRepository.findOne(entity.getId());
        if (table == null) {
            BaseResultDto.error(Constants.INFO_NOT_FOUND);
        }
        table.setStatus(entity.getStatus());
        cardNumberRepository.save(table);
        return BaseResultDto.success();
    }

    /**
     * 删除实体卡卡号
     */
    @Override
    public BaseResultDto deleteCardNumber(BaseDeleteEntities entity) {
        List<UcardCardNumberTable> tables = Lists.newArrayList();
        for (Long id : entity.getIds()) {
            UcardCardNumberTable table = new UcardCardNumberTable();
            table.setId(id);
            tables.add(table);
        }
        cardNumberRepository.deleteInBatch(tables);
        return BaseResultDto.success();
    }

    /**
     * 查询CVV发送验证码
     */
    @Override
    public void queryCvvSendCode(CardQueryCvvSendEmailCodeEntity entity) throws ServiceException {
        //验证邮箱是否存在
        existByEmail(entity.getEmail());

        //构建实体
        SysEmailSendEntity sysEmailSendEntity = createQueryCvvSendEmailCodeSysEmailEntity(entity);

        //发送收据
        emailService.sendCode(sysEmailSendEntity, sysEmailSendEntity.getMap().get(MapConstants.CODE),
                Constants.EMAIL_REGISTER_VALIDATECODE_TIME * 60 * 1000);
    }

    /**
     * 验证邮箱验证码查询cvv
     */
    @Override
    public BaseResultDto queryCvvVerifyCode(CardQueryCvvVerifyEmailCodeEntity entity) throws ServiceException {
        //验证邮箱是否存在
        existByEmail(entity.getEmail());
        emailService.validateCode(entity.getEmail(), entity.getEmailCode());
        UcardCardTable table = cardRepository.getByCardNumber(entity.getCardNumber());
        return BaseResultDto.success(Constants.SUCCESS, table.getCvv());
    }

    /**
     * 添加宣传视频
     */
    @Override
    public BaseResultDto cardFileAdd(CardFileAddEntity entity) {
        CardFileTable table = new CardFileTable();
        BeanUtils.copyProperties(entity, table);
        cardFileMapper.insert(table);
        return BaseResultDto.success();
    }

    /**
     * 修改宣传视频
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto cardFileUpdate(CardFileUpdateEntity entity) {
        CardFileTable table = cardFileRepository.findFirstById(entity.getId());
        if (table == null) {
            return BaseResultDto.error(Constants.INFO_NOT_FOUND);
        }
        BeanUtils.copyProperties(entity, table);
        cardFileRepository.save(table);
        return BaseResultDto.success();
    }

    /**
     * 删除宣传视频
     */
    @Override
    public BaseResultDto cardFileDelete(BaseDeleteEntities entity) {
        List<CardFileTable> fileTables = Lists.newArrayList();
        for (Long id : entity.getIds()) {
            CardFileTable fileTable = new CardFileTable();
            fileTable.setId(id);
            fileTables.add(fileTable);
        }
        cardFileRepository.deleteInBatch(fileTables);
        return BaseResultDto.success();
    }

    /**
     * 文件分页列表
     */
    @Override
    public ResultWithPagerDto<CardFileDto> cardFileSearchList(CardFilePageEntity entity) throws ServiceException {

        try {
            // 获取总数
            Integer count = cardFileMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }
            List<CardFileDto> dtos = cardFileMapper.pageList(entity);
            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("获取文件分页列表失败", e);
            throw new ServiceException("获取文件分页列表失败");
        }
    }

    /**
     * 全球速汇-查询支持的币种
     */
    public BaseResultDto<List<WasabiAvailableCurrenciesDto>> getAvailableCurrencies() throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getAvailableCurrenciesUrl(), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<List<WasabiAvailableCurrenciesDto>> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<List<WasabiAvailableCurrenciesDto>>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }
            List<WasabiAvailableCurrenciesDto> dtos = resultData.getData();
            return BaseResultDto.success(dtos);
        } catch (Exception e) {
            log.error("全球速汇-查询支持的币种失败", e);
            throw new ServiceException("全球速汇-查询支持的币种失败");
        }
    }

    /**
     * 全球速汇-币种发送交易需要的字段
     */
    public BaseResultDto<WasabiAvailableFieldParamsDto> getAvailableFieldParams(WasabiAvailableFieldParamsEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getAvailableFieldParamsUrl(),
                            JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiAvailableFieldParamsDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiAvailableFieldParamsDto>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }
            WasabiAvailableFieldParamsDto dto = resultData.getData();
            return BaseResultDto.success(dto);
        } catch (Exception e) {
            log.error("全球速汇-查询币种发送交易需要的字段失败", e);
            throw new ServiceException("全球速汇-查询币种发送交易需要的字段失败");
        }
    }

    /**
     * 全球速汇-查询省份/州列表
     */
    public BaseResultDto<List<WasabiStatesDto>> getStates() throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getStatesUrl(), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<List<WasabiStatesDto>> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<List<WasabiStatesDto>>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }
            List<WasabiStatesDto> dto = resultData.getData();
            return BaseResultDto.success(dto);
        } catch (Exception e) {
            log.error("全球速汇-查询省份/州列表失败", e);
            throw new ServiceException("全球速汇-查询省份/州列表失败");
        }
    }

    /**
     * 全球速汇-银行城市名单
     */
    public BaseResultDto<List<WasabiMethodDto>> getAvailableBankCities(WasabiAvailableBankCitiesEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getAvailableBankCitiesUrl(),
                            JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<List<WasabiMethodDto>> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<List<WasabiMethodDto>>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }
            List<WasabiMethodDto> dto = resultData.getData();
            return BaseResultDto.success(dto);
        } catch (Exception e) {
            log.error("全球速汇-查询银行城市名单失败", e);
            throw new ServiceException("全球速汇-查询银行城市名单失败");
        }
    }

    /**
     * 全球速汇-法币汇率
     */
    public BaseResultDto<List<WasabiExchangeRateDto>> getExchangeRate() throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getExchangeRateUrl(), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<List<WasabiExchangeRateDto>> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<List<WasabiExchangeRateDto>>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }
            List<WasabiExchangeRateDto> dto = resultData.getData();
            return BaseResultDto.success(dto);
        } catch (Exception e) {
            log.error("全球速汇-查询法币汇率失败", e);
            throw new ServiceException("全球速汇-查询法币汇率失败");
        }
    }

    /**
     * 全球速汇-发送交易
     */
    public WasabiTransactionTransferDto transactionTransfer(WasabiTransactionTransferEntity entity,
                                                            WasabiConfig config) throws Exception {
        TwoTuple<String, String> result =
                WasabiCommonUtil.post(config.getApiUrl() + config.getTransactionTransferUrl(),
                        JSON.toJSONString(entity), config);
        verifySignature(result, config.getWsbPublicKey());
        ResultData<WasabiTransactionTransferDto> resultData = JSON.parseObject(result.getSecond(),
                new TypeReference<ResultData<WasabiTransactionTransferDto>>() {
                }
        );
        if (!resultData.isSuccess()) {
            throw new ServiceException(resultData.getMsg());
        }
        WasabiTransactionTransferDto dto = resultData.getData();
        return dto;
    }

    /**
     * 全球速汇-交易记录查询
     */
    public BaseResultDto<WasabiTransactionTransferDto> getTransactionList(WasabiTransactionListEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getTransactionListUrl(),
                            JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiTransactionTransferDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiTransactionTransferDto>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }
            WasabiTransactionTransferDto dto = resultData.getData();
            return BaseResultDto.success(dto);
        } catch (Exception e) {
            log.error("全球速汇-交易记录查询失败", e);
            throw new ServiceException("全球速汇-交易记录查询失败");
        }
    }

    /**
     * 全球速汇-交易详情查询
     */
    public BaseResultDto<WasabiTransactionDetailDto> getTransactionDetail(WasabiTransactionDetailEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getTransactionDetailUrl(),
                            JSON.toJSONString(entity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiTransactionDetailDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiTransactionDetailDto>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }
            WasabiTransactionDetailDto dto = resultData.getData();
            return BaseResultDto.success(dto);
        } catch (Exception e) {
            log.error("全球速汇-交易详情查询失败", e);
            throw new ServiceException("全球速汇-交易详情查询失败");
        }
    }

    /**
     * 全球速汇-提交合规性检查资料
     */
    public BaseResultDto transactionSubmitRfi(WasabiTransactionSubmitRfiEntity entity) throws ServiceException {
        WasabiConfig config = getWasabiConfig();
        try {
            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getSubmitRfiUrl(), JSON.toJSONString(entity),
                            config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData>() {
                    }
            );
            if (!resultData.isSuccess()) {
                throw new ServiceException(resultData.getMsg());
            }
            return BaseResultDto.success();
        } catch (Exception e) {
            log.error("全球速汇-提交合规性检查资料失败", e);
            throw new ServiceException("全球速汇-提交合规性检查资料失败");
        }
    }

    /**
     * 速汇-更新支持的币种、银行城市名单、州列表、币种发送交易需要的字段
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto saveAvailableCurrencies() throws ServiceException {
        // 删除州表数据
        globalStatesMapper.deleteAll();
        // 删除支持的币种数据
        globalCurrenciesMapper.deleteAll();
        // 删除币种发送交易需要的字段
        globalCurrenciesFieldMapper.deleteAll();
        // 删除字段可选值
        globalCurrenciesFieldAvailableMapper.deleteAll();
        // 删除银行城市名单
        globalAvailableBankCitiesMapper.deleteAll();
        Map<String, String> countryCodeMap = null;

        // 查询省/州数据
        List<WasabiStatesDto> statesDtos = getStates().getData();
        if (CollectionUtils.isNotEmpty(statesDtos)) {
            List<List<WasabiStatesDto>> statesLists = ListSplitUtil.split(statesDtos);
            for (List<WasabiStatesDto> statesList : statesLists) {
                globalStatesMapper.batchSave(statesList);
            }
            // 提取为map<getCountryStandardCode,getCode> 通过国家查询出省/州的code 使用code和币种查询城市列表
            countryCodeMap = statesDtos.stream()
                    .collect(Collectors.toMap(
                            WasabiStatesDto::getCountryStandardCode,  // key: countryStandardCode
                            WasabiStatesDto::getCode,                 // value: code
                            (existing, replacement) -> existing       // 处理重复key，保留第一个
                    ));
        }
        // 查询支持的币种
        List<WasabiAvailableCurrenciesDto> currenciesDtos = getAvailableCurrencies().getData();
        if (CollectionUtils.isEmpty(currenciesDtos)) {
            return BaseResultDto.success();
        }
        List<CardGlobalCurrenciesTable> currenciesTables = currenciesDtos.stream()
                .map(currenciesDto -> {
                    CardGlobalCurrenciesTable currenciesTable = new CardGlobalCurrenciesTable();
                    BeanUtils.copyProperties(currenciesDto, currenciesTable);
                    currenciesTable.setTransferTypeId(currenciesDto.getTransferType().getValueId());
                    currenciesTable.setTransferTypeValue(currenciesDto.getTransferType().getValue());
                    currenciesTable.setPayoutMethodId(currenciesDto.getPayoutMethod().getValueId());
                    currenciesTable.setPayoutMethodValue(currenciesDto.getPayoutMethod().getValue());
                    currenciesTable.setAddTime(new Date());
                    return currenciesTable;
                }).distinct().collect(Collectors.toList());// 添加去重操作
        if (CollectionUtils.isNotEmpty(currenciesTables)) {
            List<CardGlobalAvailableBankCitiesTable> bankCitiesTables = new ArrayList<>();
            // 遍历币种查询支持的字段
            for (CardGlobalCurrenciesTable currenciesTable : currenciesTables) {
                WasabiAvailableFieldParamsEntity fieldParamsEntity = new WasabiAvailableFieldParamsEntity();
                fieldParamsEntity.setPayoutCurrency(currenciesTable.getPayoutCurrency());
                fieldParamsEntity.setTransferType(currenciesTable.getTransferTypeId());
                fieldParamsEntity.setPayoutMethod(currenciesTable.getPayoutMethodId());
                // 查询币种发送交易需要的字段
                WasabiAvailableFieldParamsDto availableFieldParamsDto =
                        getAvailableFieldParams(fieldParamsEntity).getData();
                // 保存单条币种数据 返回ID 关联到 字段表的 payout_currency_id
                Long currenciesTableId = globalCurrenciesRepository.save(currenciesTable).getId();
                List<WasabiFieldParamsDto> fieldParams = availableFieldParamsDto.getFieldParams();
                if (CollectionUtils.isNotEmpty(fieldParams)) {
                    fieldParams = fieldParams.stream().distinct().collect(Collectors.toList());
                    // 遍历字段数据 获取字段可选值列表
                    for (WasabiFieldParamsDto fieldParamsDto : fieldParams) {
                        CardGlobalCurrenciesFieldTable currenciesFieldTable = new CardGlobalCurrenciesFieldTable();
                        BeanUtils.copyProperties(fieldParamsDto, currenciesFieldTable);
                        currenciesFieldTable.setPayoutCurrencyId(currenciesTableId);
                        currenciesFieldTable.setAddTime(new Date());
                        // 保存单条字段数据 返回ID 关联到 字段可选值表的 field_id
                        Long fieldParamsTableId = globalCurrenciesFieldRepository.save(currenciesFieldTable).getId();
                        List<WasabiMethodDto> selectValues = fieldParamsDto.getSelectValues();
                        if (CollectionUtils.isNotEmpty(selectValues)) {
                            selectValues = selectValues.stream().distinct().collect(Collectors.toList());
                            List<CardGlobalCurrenciesFieldAvailableTable> fieldAvailableTables = new ArrayList<>();
                            for (WasabiMethodDto selectValue : selectValues) {
                                CardGlobalCurrenciesFieldAvailableTable fieldAvailableTable =
                                        new CardGlobalCurrenciesFieldAvailableTable();
                                fieldAvailableTable.setFieldId(fieldParamsTableId);
                                fieldAvailableTable.setValueId(selectValue.getValueId());
                                fieldAvailableTable.setValue(selectValue.getValue());
                                fieldAvailableTables.add(fieldAvailableTable);
                            }
                            if (CollectionUtils.isNotEmpty(fieldAvailableTables)) {
                                List<List<CardGlobalCurrenciesFieldAvailableTable>> lists =
                                        ListSplitUtil.split(fieldAvailableTables);
                                for (List<CardGlobalCurrenciesFieldAvailableTable> fieldAvailableList : lists) {
                                    globalCurrenciesFieldAvailableMapper.batchSave(fieldAvailableList);
                                }
                            }
                        }
                    }
                }
                // 处理币种支持的城市列表
                if (StringUtils.isNotEmpty(currenciesTable.getPayoutCurrency()) && countryCodeMap != null) {
                    WasabiAvailableBankCitiesEntity bankCitiesEntity = new WasabiAvailableBankCitiesEntity();
                    bankCitiesEntity.setPayoutCurrency(currenciesTable.getPayoutCurrency());
                    bankCitiesEntity.setParentCodeId(countryCodeMap.get(currenciesTable.getCountry()));
                    List<WasabiMethodDto> data = getAvailableBankCities(bankCitiesEntity).getData();
                    if (CollectionUtils.isNotEmpty(data)) {
                        for (WasabiMethodDto methodDto : data) {
                            CardGlobalAvailableBankCitiesTable bankCitiesTable =
                                    new CardGlobalAvailableBankCitiesTable();
                            bankCitiesTable.setPayoutCurrency(bankCitiesEntity.getPayoutCurrency());
                            bankCitiesTable.setParentCodeId(bankCitiesEntity.getParentCodeId());
                            bankCitiesTable.setBankCitiesKey(methodDto.getValueId());
                            bankCitiesTable.setBankCitiesValue(methodDto.getValue());
                            bankCitiesTables.add(bankCitiesTable);
                        }
                    }
                }
            }
            if (CollectionUtils.isNotEmpty(bankCitiesTables)) {
                List<List<CardGlobalAvailableBankCitiesTable>> bankCitiesLists = ListSplitUtil.split(bankCitiesTables);
                for (List<CardGlobalAvailableBankCitiesTable> bankCitiesList : bankCitiesLists) {
                    globalAvailableBankCitiesMapper.batchSave(bankCitiesList);
                }
            }
        }
        return BaseResultDto.success();
    }

    /**
     * 币种分页列表
     */
    @Override
    public ResultWithPagerDto<WasabiAvailableCurrenciesListDto> getCurrenciesPageList(GlobalCurrenciesPageListEntity entity) throws ServiceException {
        try {
            // 获取总数
            Integer count = globalCurrenciesMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }
            List<WasabiAvailableCurrenciesListDto> dtos = globalCurrenciesMapper.pageList(entity);
            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("币种分页列表查询失败", e);
            throw new ServiceException("币种分页列表查询失败");
        }
    }

    /**
     * 查询币种全局配置
     */
    @Override
    public BaseResultDto<WasabiAvailableCurrenciesListDto> getCurrenciesGlobalConfig() throws ServiceException {
        WasabiAvailableCurrenciesListDto dto = new WasabiAvailableCurrenciesListDto();
        CardFeeRateConfigTable table = globalFeeRateConfigRepository.findByType("1");
        if (table != null) {
            BeanUtils.copyProperties(table, dto);
        }
        return BaseResultDto.success(dto);
    }

    /**
     * 根据币种信息获取实时汇率
     */
    @Override
    public BigDecimal getExchangeRateByCurrency(String payoutCurrency, String transferType, String payoutMethod) throws ServiceException {
        // 1. 获取三方接口返回的汇率数据
        BaseResultDto<List<WasabiExchangeRateDto>> wasabiResult = getExchangeRate();
        if (wasabiResult.getCode() != 200 || wasabiResult.getData() == null) {
            throw ServiceException.exception(wasabiResult.getMsg());
        }
        List<WasabiExchangeRateDto> wasabiRates = wasabiResult.getData();

        // 2. 创建映射以便快速查找三方数据
        // key: payoutCurrency_transferTypeId_payoutMethodId_country
        Map<String, WasabiExchangeRateDto> wasabiMap = wasabiRates.stream()
                .collect(Collectors.toMap(
                        rate -> String.join("_",
                                rate.getPayoutCurrency() != null ? rate.getPayoutCurrency() : "",
                                rate.getTransferType() != null ? rate.getTransferType().getValueId() : "",
                                rate.getPayoutMethod() != null ? rate.getPayoutMethod().getValueId() : ""),
                        rate -> rate,
                        (existing, replacement) -> existing // 处理重复key，保留第一个
                ));
        // 2. 拼接key 进行比对
        String key = String.join("_",
                payoutCurrency != null ? payoutCurrency : "",
                transferType != null ? transferType : "",
                payoutMethod != null ? payoutMethod : "");
        WasabiExchangeRateDto rateDto = wasabiMap.get(key);
        return rateDto.getExchangeRate();
    }

    /**
     * 查询支持的币种下拉列表
     */
    @Override
    public BaseResultDto<List<GlobalCurrenciesDropdownListDto>> getCurrenciesDropdown(GlobalCurrenciesDropdownEntity entity) throws ServiceException {
        List<GlobalCurrenciesDropdownListDto> dtos = globalCurrenciesMapper.findAllByStatus(entity.getStatus());
        return BaseResultDto.success(dtos);
    }

    /**
     * 根据币种id查询交易需要的字段和可选值列表
     */
    @Override
    public BaseResultDto<List<WasabiFieldDto>> getCurrenciesFieldList(GlobalCurrenciesFieldListEntity entity) throws ServiceException {
        List<WasabiFieldDto> dtos = globalCurrenciesFieldMapper.findAllByCurrencies(entity.getPayoutCurrency(),
                entity.getTransferTypeId(), entity.getPayoutMethodId(), null);
        if (CollectionUtils.isNotEmpty(dtos)) {
            for (WasabiFieldDto dto : dtos) {
                List<CardGlobalCurrenciesFieldAvailableTable> tables =
                        globalCurrenciesFieldAvailableRepository.findAllByFieldId(dto.getId());
                if (CollectionUtils.isNotEmpty(tables)) {
                    List<WasabiFieldAvailableDto> availableDtos = new ArrayList<>();
                    for (CardGlobalCurrenciesFieldAvailableTable table : tables) {
                        WasabiFieldAvailableDto availableDto = new WasabiFieldAvailableDto();
                        BeanUtils.copyProperties(table, availableDto);
                        availableDtos.add(availableDto);
                    }
                    dto.setAvailableDtos(availableDtos);
                }
            }
        }
        return BaseResultDto.success(dtos);
    }

    /**
     * 查询汇率计算手续费
     */
    @Override
    public GlobalQueryExchangeRateDto queryExchangeRate(GlobalQueryExchangeRateEntity entity) throws ServiceException {
        GlobalQueryExchangeRateDto result = new GlobalQueryExchangeRateDto();
        // 获取汇率
        BigDecimal exchangeRate = getExchangeRateByCurrency(entity.getPayoutCurrency(),
                entity.getTransferTypeId(), entity.getPayoutMethodId());
        CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(entity.getUniqueId());
        CardFeeRateConfigTable configTable =
                globalFeeRateConfigRepository.getByPayoutCurrencyAndTransferTypeIdAndPayoutMethodIdAndType(entity.getPayoutCurrency(), entity.getTransferTypeId(), entity.getPayoutMethodId(), "2");
        CardGlobalCurrenciesTable currenciesTable =
                globalCurrenciesRepository.findByPayoutCurrencyAndTransferTypeIdAndPayoutMethodId(entity.getPayoutCurrency()
                        , entity.getTransferTypeId(), entity.getPayoutMethodId());
        if (configTable == null) {
            configTable = new CardFeeRateConfigTable();
            configTable.setMinQuotaConfig(currenciesTable.getMinQuota().divide(exchangeRate, 10, RoundingMode.DOWN));
            configTable.setMaxQuotaConfig(currenciesTable.getMaxQuota().divide(exchangeRate, 10, RoundingMode.DOWN));
            configTable.setYearTransferAmountQuotaConfig(currenciesTable.getYearTransferAmountQuota().divide(exchangeRate, 10, RoundingMode.DOWN));
        }
        if (configTable.getMinQuotaConfig() == null) {
            configTable.setMinQuotaConfig(currenciesTable.getMinQuota().divide(exchangeRate, 10, RoundingMode.DOWN));
        }
        if (configTable.getMaxQuotaConfig() == null) {
            configTable.setMaxQuotaConfig(currenciesTable.getMaxQuota().divide(exchangeRate, 10, RoundingMode.DOWN));
        }
        if (configTable.getYearTransferAmountQuotaConfig() == null) {
            configTable.setYearTransferAmountQuotaConfig(currenciesTable.getYearTransferAmountQuota().divide(exchangeRate, 10, RoundingMode.DOWN));
        }
        // 查询手续费率 固定手续费 计算手续费
        GlobalCalculateFeeDto feeDto = calculateFee(userTable, entity.getAmount(), configTable);
        // 已用额度
        BigDecimal allAmount = globalOrderMapper.findAllByCId(userTable.getCId(), entity.getPayoutCurrency(),
                entity.getTransferTypeId(), entity.getPayoutMethodId());
        BigDecimal minQuota = configTable.getMinQuotaConfig().setScale(0, RoundingMode.UP);
        BigDecimal maxQuota = configTable.getMaxQuotaConfig().setScale(0, RoundingMode.DOWN);
        result.setFeeRate(feeDto.getFeeRate());
        result.setFixedFee(feeDto.getFixedFee());
        result.setDeductionFee(feeDto.getDeductionFee());
        result.setMinQuota(minQuota.compareTo(new BigDecimal("20")) < 0 ? new BigDecimal("20") : minQuota);
        result.setMaxQuota(maxQuota.compareTo(new BigDecimal("20")) < 0 ? new BigDecimal("20") : maxQuota);
        result.setYearTransferAmountQuota(configTable.getYearTransferAmountQuotaConfig());
        result.setExchangeRate(exchangeRate);
        result.setUsedQuota(allAmount);
        if (entity.getAmount() != null) {
            validateDecimalPlaces(entity.getAmount(), "amount", 2);
            // 计算到账金额
            BigDecimal transferAmount = entity.getAmount().multiply(exchangeRate).setScale(2, RoundingMode.DOWN);
            result.setTransferAmount(transferAmount);
            if (entity.getAmount().compareTo(result.getMinQuota() == null ? BigDecimal.ZERO : result.getMinQuota()) < 0) {
                throw ServiceException.exception("不能小于单笔转账最小额度");
            }
            if (entity.getAmount().compareTo(result.getMaxQuota() == null ? BigDecimal.ZERO : result.getMaxQuota()) > 0) {
                throw ServiceException.exception("不能大于单笔转账最大额度");
            }
            if ((allAmount).compareTo(result.getYearTransferAmountQuota() == null ? BigDecimal.ZERO :
                    result.getYearTransferAmountQuota()) > 0) {
                throw ServiceException.exception("不能大于年度转账金额");
            }
        }
        return result;
    }

    /**
     * 币种配置更改
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto globalCurrenciesConfig(GlobalCurrenciesConfigEntity entity) throws ServiceException {
        if (entity.getMinQuotaConfig() != null &&
                entity.getMinQuotaConfig().compareTo(new BigDecimal("20")) < 0) {
            throw new ServiceException("单笔转账最小额度不能小于20");
        }
        if (entity.getMaxQuotaConfig() != null &&
                entity.getMaxQuotaConfig().compareTo(new BigDecimal("20")) < 0) {
            throw new ServiceException("单笔转账最大额度不能小于20");
        }
        if (entity.getYearTransferAmountQuotaConfig() != null &&
                entity.getYearTransferAmountQuotaConfig().compareTo(new BigDecimal("20")) < 0) {
            throw new ServiceException("年度转账金额额度不能小于20");
        }
        if (entity.getMaxQuotaConfig() != null &&
                entity.getMinQuotaConfig() != null &&
                entity.getMaxQuotaConfig().compareTo(entity.getMinQuotaConfig()) <= 0) {
            throw new ServiceException("单笔转账最大额度必须大于最小额度");
        }
        if (entity.getYearTransferAmountQuotaConfig() != null &&
                entity.getMinQuotaConfig() != null &&
                entity.getYearTransferAmountQuotaConfig().compareTo(entity.getMinQuotaConfig()) <= 0) {
            throw new ServiceException("年度转账金额额度必须大于单笔转账最小额度");
        }
        if (entity.getFeeRateConfig() != null) {
            validateDecimalPlaces(entity.getFeeRateConfig(), "feeRateConfig", 10);
        }
        if (entity.getFixedFeeConfig() != null) {
            validateDecimalPlaces(entity.getFixedFeeConfig(), "fixedFeeConfig", 10);
        }
        CardFeeRateConfigTable table = null;
        if ("2".equals(entity.getType())) {
            CardGlobalCurrenciesTable currenciesTable =
                    globalCurrenciesRepository.findByPayoutCurrencyAndTransferTypeIdAndPayoutMethodId(entity.getPayoutCurrency(), entity.getTransferTypeId(), entity.getPayoutMethodId());
            if (currenciesTable == null) {
                throw ServiceException.exception(Constants.INFO_NOT_FOUND);
            }
            BigDecimal exchangeRate = entity.getExchangeRate();
            // 校验单笔最小额度配置
            if (entity.getMinQuotaConfig() != null) {
                validateDecimalPlaces(entity.getMinQuotaConfig(), "minQuotaConfig", 10);
                BigDecimal minQuotaAmount = entity.getMinQuotaConfig().multiply(exchangeRate);
                if (minQuotaAmount.compareTo(currenciesTable.getMinQuota()) < 0) {
                    throw ServiceException.exception("配置的单笔最小额度不能小于系统最小额度");
                }
                if (minQuotaAmount.compareTo(currenciesTable.getMaxQuota()) > 0) {
                    throw ServiceException.exception("配置的单笔最小额度不能大于系统单笔最大额度");
                }
            }

            // 校验单笔最大额度配置
            if (entity.getMaxQuotaConfig() != null) {
                validateDecimalPlaces(entity.getMaxQuotaConfig(), "maxQuotaConfig", 10);
                BigDecimal maxQuotaAmount = entity.getMaxQuotaConfig().multiply(exchangeRate);
                if (maxQuotaAmount.compareTo(currenciesTable.getMaxQuota()) > 0) {
                    throw ServiceException.exception("配置的单笔最大额度不能大于系统最大额度");
                }

                if (maxQuotaAmount.compareTo(currenciesTable.getMinQuota()) < 0) {
                    throw ServiceException.exception("配置的单笔最大额度不能小于系统最小额度");
                }
            }

            // 校验年度转账金额额度配置
            if (entity.getYearTransferAmountQuotaConfig() != null) {
                validateDecimalPlaces(entity.getYearTransferAmountQuotaConfig(), "yearTransferAmountQuotaConfig"
                        , 10);
                BigDecimal yearTransferAmountQuotaAmount =
                        entity.getYearTransferAmountQuotaConfig().multiply(exchangeRate);

                if (yearTransferAmountQuotaAmount.compareTo(currenciesTable.getYearTransferAmountQuota()) > 0) {
                    throw ServiceException.exception("配置的年度转账金额不能大于系统年度额度");
                }
                if (yearTransferAmountQuotaAmount.compareTo(currenciesTable.getMinQuota()) < 0) {
                    throw ServiceException.exception("配置的年度转账金额不能小于系统单笔最小额度");
                }
            }

            table =
                    globalFeeRateConfigRepository.findByPayoutCurrencyAndTransferTypeIdAndPayoutMethodIdAndType(entity.getPayoutCurrency(), entity.getTransferTypeId(), entity.getPayoutMethodId(), entity.getType());

        } else if ("1".equals(entity.getType())) {
            table = globalFeeRateConfigRepository.findByType(entity.getType());
        }

        if (table == null) {
            table = new CardFeeRateConfigTable();
            table.setAddTime(new Date());
        } else {
            table.setModifyTime(new Date());
        }
        BeanUtils.copyProperties(entity, table);
        globalFeeRateConfigRepository.save(table);
        return BaseResultDto.success();
    }

    /**
     * 查询支持的城市列表
     */
    @Override
    public BaseResultDto<List<CardBankCitiesDto>> queryBankCities(CardBankCitiesEntity entity) throws ServiceException {
        List<CardBankCitiesDto> dtos =
                globalAvailableBankCitiesMapper.queryAllByCountryAndPayoutCurrency(entity.getCountry(),
                        entity.getPayoutCurrency());
        return BaseResultDto.success(dtos);
    }

    /**
     * 查询账户信息:钱包 和 卡
     */
    @Override
    public BaseResultDto cardAccountDropdown(CardAccountDropdownEntity entity) throws ServiceException {
        List<CardAccountInfoDto> dtos = new ArrayList<>();
        Long cId = entity.getCId();
        CardMerchantUserTable userTable = merchantUserRepository.findByCId(cId);
        if (userTable == null) {
            return BaseResultDto.success(dtos);
        }
        // 查询钱包余额
        UcardWalletTable walletTable = ucardWalletRepository.getFirstByCId(cId);
        if (walletTable != null) {
            CardAccountInfoDto dto = new CardAccountInfoDto();
            dto.setType("2");
            dto.setBalance(walletTable.getBalance());
            dto.setCardNumber(String.valueOf(walletTable.getId()));
            dtos.add(dto);
        }
        List<UcardCardTable> cardTables = cardMapper.findAllByUniqueId(userTable.getUniqueId());
        if (CollectionUtils.isNotEmpty(cardTables)) {
            for (UcardCardTable table : cardTables) {
                CardAccountInfoDto dto = new CardAccountInfoDto();
                WasabiBalanceInfoEntity infoEntity = new WasabiBalanceInfoEntity();
                infoEntity.setCardNo(table.getCardNo());
                try {
                    BaseResultDto<WasabiBalanceInfoDto> balanceInfo = getBalanceInfo(infoEntity);
                    if (balanceInfo.getCode() == 200) {
                        WasabiBalanceInfoDto infoDto = balanceInfo.getData();
                        dto.setType("1");
                        dto.setBalance(infoDto.getAmount());
                        dto.setCardNumber(table.getCardNumber());
                        dtos.add(dto);
                    }

                } catch (ServiceException e) {
                    log.error("全球速汇获取账户信息失败 - cardNumber{} ", table.getCardNumber(), e);
                }
            }
        }
        return BaseResultDto.success(dtos);
    }

    /**
     * 全球速汇-创建交易订单
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto globalCreateOrder(GlobalCreateOrderEntity entity) throws Exception {
        if (entity.getTransferAmount().compareTo(BigDecimal.ZERO) <= 0 ||
                entity.getAmount().compareTo(new BigDecimal("20")) < 0) {
            throw ServiceException.exception(Constants.PARAMETER_ERROR);
        }
        CardMerchantUserTable userTable = merchantUserRepository.findByUniqueId(entity.getUniqueId());

        BigDecimal allAmount = globalOrderMapper.findAllByCId(userTable.getCId(), entity.getPayoutCurrency(),
                entity.getTransferType(), entity.getPayoutMethod());
        CardFeeRateConfigTable rateConfigTable =
                globalFeeRateConfigRepository.getByPayoutCurrencyAndTransferTypeIdAndPayoutMethodIdAndType(entity.getPayoutCurrency(), entity.getTransferType(),
                        entity.getPayoutMethod(), "2");

        if (rateConfigTable == null || rateConfigTable.getYearTransferAmountQuotaConfig() == null) {
            rateConfigTable = new CardFeeRateConfigTable();
            BigDecimal exchangeRate = getExchangeRateByCurrency(entity.getPayoutCurrency(),
                    entity.getTransferType(), entity.getPayoutMethod());
            CardGlobalCurrenciesTable currenciesTable =
                    globalCurrenciesRepository.findByPayoutCurrencyAndTransferTypeIdAndPayoutMethodId(entity.getPayoutCurrency(), entity.getTransferType(), entity.getPayoutMethod());
            rateConfigTable.setYearTransferAmountQuotaConfig(currenciesTable.getYearTransferAmountQuota().divide(exchangeRate, 10, RoundingMode.DOWN));
        }
        BigDecimal yearAmount = rateConfigTable.getYearTransferAmountQuotaConfig();

        if ((entity.getAmount().add(allAmount)).compareTo(yearAmount) > 0) {
            return BaseResultDto.error("超出年度转账金额");
        }
        // 总扣款金额 = 扣款金额 + 手续费
        BigDecimal totalAmount = entity.getAmount().add(entity.getDeductionFee());
        String deductionMerchantOrderNo = null;
        String deductionAccountStatus = null;
        WasabiConfig config = getWasabiConfig();
        String merchantOrderNo = ObjectIdUtil.genSnowFlakeIdStr("GT");
        if ("2".equals(entity.getDeductionAccountType())) {
            UcardWalletTable walletTable = ucardWalletRepository.getFirstById(Long.valueOf(entity.getCardNumber()));
            if (walletTable.getBalance().compareTo(totalAmount) < 0) {
                return BaseResultDto.error("余额不足");
            }
            walletTable.setBalance(walletTable.getBalance().subtract(totalAmount));
            ucardWalletRepository.save(walletTable);
            UcardWalletRecordTable walletRecordTable = new UcardWalletRecordTable();
            walletRecordTable.setWalletId(walletTable.getId());
            walletRecordTable.setUniqueId(walletTable.getUniqueId());
            walletRecordTable.setCId(walletTable.getCId());
            walletRecordTable.setAmount(entity.getAmount().negate());
            walletRecordTable.setFee(entity.getDeductionFee().negate());
            walletRecordTable.setSerial(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
            walletRecordTable.setStatus(2);
            walletRecordTable.setType(7);
            walletRecordTable.setAddTime(new Date());
            ucardWalletRecordRepository.save(walletRecordTable);
            // 将收款人信息保存到数据库
            CardGlobalReceiverInfoTable receiverInfoTable =
                    globalReceiverInfoRepository.findByUniqueIdAndReceiverBankAccountNumber(entity.getUniqueId(),
                            entity.getReceiverBankAccountNumber());
            if (receiverInfoTable == null) {
                receiverInfoTable = new CardGlobalReceiverInfoTable();
            }
            BeanUtilPlus.copyPropertiesIgnoreNull(entity, receiverInfoTable);
            globalReceiverInfoRepository.save(receiverInfoTable);
            deductionAccountStatus = "2";
        } else if ("1".equals(entity.getDeductionAccountType())) {
            UcardCardTable cardTable = cardRepository.getByCardNumber(entity.getCardNumber());
            WasabiBalanceInfoEntity infoEntity = new WasabiBalanceInfoEntity();
            infoEntity.setCardNo(cardTable.getCardNo());
            WasabiBalanceInfoDto infoDto = getBalanceInfo(infoEntity).getData();
            if (infoDto.getAmount().compareTo(totalAmount) < 0) {
                return BaseResultDto.error("余额不足");
            }
            deductionMerchantOrderNo = ObjectIdUtil.genSnowFlakeIdStr(config.getMerchantTag());
            WasabiBalanceEntity balanceEntity = new WasabiBalanceEntity();
            balanceEntity.setCardNo(cardTable.getCardNo());
            balanceEntity.setMerchantOrderNo(deductionMerchantOrderNo);
            balanceEntity.setAmount(totalAmount);
            ResultData<WasabiDepositDto> resultData = withdraw(balanceEntity, config);
            if (!resultData.isSuccess()) {
                return BaseResultDto.error(resultData.getMsg());
            }

            WasabiDepositDto dto = resultData.getData();
            CardWithdrawTable table = new CardWithdrawTable();
            BeanUtils.copyProperties(dto, table);
            table.setAmount(totalAmount.negate());
            table.setType("1");
            table.setUniqueId(cardTable.getUniqueId());
            table.setCardNumber(cardTable.getCardNumber());
            table.setAddTime(new Date());
            withdrawRepository.save(table);

            deductionAccountStatus = GlobalOrderDeductionTypeEnum.matchCode(dto.getStatus());
        }
        CardGlobalOrderTable table = new CardGlobalOrderTable();
        BeanUtils.copyProperties(entity, table);
        table.setMerchantOrderNo(merchantOrderNo);
        table.setCustomId(userTable.getCustomId());
        table.setUniqueId(userTable.getUniqueId());
        table.setDeductionAmount(entity.getAmount());
        table.setDeductionMerchantOrderNo(deductionMerchantOrderNo);
        table.setDeductionAccountStatus(deductionAccountStatus);
        table.setApproveStatus(1);
        table.setSendCurrency("USD");
        table.setAddTime(new Date());
        globalOrderRepository.save(table);
        CardOperateTable operateTable = new CardOperateTable();
        operateTable.setUniqueId(userTable.getUniqueId());
        operateTable.setStatus(Constants.SUCCESS);
        operateTable.setOperateType(19);
        operateTable.setOperateUser(entity.getCId());
        operateTable.setAddTime(new Date());
        cardOperateRepository.save(operateTable);
        // 给审核人员发邮件
        sendGlobalOrderReviewEmail(userTable, table);
        return BaseResultDto.success();
    }

    /**
     * 取消交易订单
     */
    @Override
    @Transactional
    public BaseResultDto globalCancelOrder(GlobalCancelOrderEntity entity) throws Exception {
        CardGlobalOrderTable table = globalOrderRepository.findFirstById(entity.getId());
        if (table == null) {
            return BaseResultDto.error(Constants.INFO_NOT_FOUND);
        }
        if (table.getApproveStatus() != 1) {
            return BaseResultDto.error("The order is not pending review");
        }
        if (GlobalOrderStatusEnum.CANCEL.getStatus().equals(table.getStatus())) {
            return BaseResultDto.error("The current order has been cancelled");
        }
        if (!"2".equals(table.getDeductionAccountStatus())) {
            return BaseResultDto.error("请等待扣款完成再取消订单");
        }
        table.setStatus(GlobalOrderStatusEnum.CANCEL.getStatus());
        table.setModifyTime(new Date());
        refundTransacOrder(table);
        CardOperateTable operateTable = new CardOperateTable();
        operateTable.setUniqueId(table.getUniqueId());
        operateTable.setMerchantOrderNo(table.getMerchantOrderNo());
        operateTable.setOrderNo(table.getOrderNo());
        operateTable.setStatus(Constants.SUCCESS);
        operateTable.setOperateType(21);
        operateTable.setOperateUser(entity.getCId());
        operateTable.setAddTime(new Date());
        cardOperateRepository.save(operateTable);
        CardMerchantUserTable userTable = merchantUserRepository.findByUniqueId(table.getUniqueId());
        // 订单取消给客户发送邮件
        sendGlobalOrderCancelEmail(userTable, table);
        return BaseResultDto.success();
    }

    /**
     * 计算手续费
     */
    private GlobalCalculateFeeDto calculateFee(CardMerchantUserTable userTable, BigDecimal amount,
                                               CardFeeRateConfigTable configTable) throws ServiceException {
        GlobalCalculateFeeDto dto = new GlobalCalculateFeeDto();
        BigDecimal feeRate = null;
        BigDecimal fixedFee = null;
        if (userTable.getGlobalFeeRate() != null) {
            feeRate = userTable.getGlobalFeeRate();
        }
        if (userTable.getGlobalFixedFee() != null) {
            fixedFee = userTable.getGlobalFixedFee();
        }
        if (feeRate == null && fixedFee == null) {
            feeRate = configTable.getFeeRateConfig();
            fixedFee = configTable.getFixedFeeConfig();
        }

        if (fixedFee == null) {
            fixedFee = BigDecimal.ZERO;
        }
        dto.setFeeRate(feeRate);
        dto.setFixedFee(fixedFee);
        if (amount != null) {
            // 计算平台手续费
            BigDecimal feeRateAmount;
            if (feeRate == null) {
                feeRateAmount = BigDecimal.ZERO;
            } else {
                feeRateAmount = amount.multiply(feeRate).divide(new BigDecimal(100), 10, RoundingMode.UP);
            }
            dto.setDeductionFee(feeRateAmount.add(fixedFee).setScale(2, RoundingMode.UP));
        }
        return dto;
    }

    /**
     * 查询速汇订单列表
     */
    @Override
    public ResultWithPagerDto<GlobalOrderDto> globalOrderPageList(GlobalOrderPageEntity entity) throws ServiceException {
        try {
            /**
             * 格式化时间
             */
            if (entity.getEndDate() != null) {
                entity.setEndDate(DateUtil.operationDay(entity.getEndDate(), 1));
            }
            // 获取总数
            Integer count = globalOrderMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }
            List<GlobalOrderDto> dtos = globalOrderMapper.pageList(entity);
            if (CollectionUtils.isNotEmpty(dtos)) {
                dtos.forEach(dto -> {
                    dto.setPayoutMethod(GlobalPayoutMethodEnum.matchKey(dto.getPayoutMethod()));
                    dto.setTransferType(GlobalTransferTypeEnum.matchType(dto.getTransferType()));
                });
            }
            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("速汇交易订单分页查询失败", e);
            throw new ServiceException("速汇交易订单分页查询失败");
        }
    }

    /**
     * 速汇查询用户详情
     */
    @Override
    public BaseResultDto<MerchantUserDto> findGlobalUserDetails(InfoEntity entity) throws ServiceException {
        MerchantUserDto dto = merchantUserMapper.findUserDetailsByCId(entity.getCustomInfo().getCId());
        return BaseResultDto.success(dto);
    }

    /**
     * 查询速汇订单详情
     */
    @Override
    public BaseResultDto<GlobalOrderDto> queryGlobalOrderDetails(BaseEntity entity) throws ServiceException {
        try {
            GlobalOrderDto dto = new GlobalOrderDto();
            CardGlobalOrderTable table = globalOrderRepository.getFirstById(entity.getId());
            if (table == null) {
                return BaseResultDto.error(Constants.INFO_NOT_FOUND);
            }
            BeanUtils.copyProperties(table, dto);
            dto.setTransferType(GlobalTransferTypeEnum.matchType(table.getTransferType()));
            dto.setPayoutMethod(GlobalPayoutMethodEnum.matchKey(table.getPayoutMethod()));
            List<CardGlobalOrderDataTable> orderDataTables =
                    globalOrderDataRepository.findAllByOrderNo(table.getOrderNo());
            List<GlobalOrderDataDto> orderDataDtos = new ArrayList<>();
            for (CardGlobalOrderDataTable orderDataTable : orderDataTables) {
                GlobalOrderDataDto dataDto = new GlobalOrderDataDto();
                BeanUtils.copyProperties(orderDataTable, dataDto);
                List<CardGlobalOrderDataAvailableTable> dataAvailableTables =
                        globalOrderDataAvailableRepository.findAllByOrderNoAndRfiId(orderDataTable.getOrderNo(),
                                orderDataTable.getRfiId());
                if (CollectionUtils.isNotEmpty(dataAvailableTables)) {
                    List<GlobalOrderDataAvailableDto> availableDtos = new ArrayList<>();
                    for (CardGlobalOrderDataAvailableTable dataAvailableTable : dataAvailableTables) {
                        GlobalOrderDataAvailableDto availableDto = new GlobalOrderDataAvailableDto();
                        BeanUtils.copyProperties(dataAvailableTable, availableDto);
                        availableDtos.add(availableDto);
                    }
                    if (CollectionUtils.isNotEmpty(availableDtos)) {
                        dataDto.setAvailableDtos(availableDtos);
                    }
                }
                orderDataDtos.add(dataDto);
            }
            dto.setDataDtos(orderDataDtos);
            List<WasabiFieldDto> fieldDtos =
                    globalCurrenciesFieldMapper.findAllByCurrencies(table.getPayoutCurrency(), table.getTransferType(),
                            table.getPayoutMethod(), null);
            dto.setFieldDtos(fieldDtos);

            return BaseResultDto.success(dto);
        } catch (Exception e) {
            log.error("速汇交易订单详情查询失败", e);
            throw new ServiceException("速汇交易订单详情查询失败");
        }
    }

    /**
     * 速汇订单审批
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto globalOrderApprove(GlobalOrderApproveEntity entity) throws Exception {
        CardGlobalOrderTable table = globalOrderRepository.findFirstById(entity.getId());
        if (table == null) {
            return BaseResultDto.error(Constants.INFO_NOT_FOUND);
        }
        if (!"2".equals(table.getDeductionAccountStatus())) {
            return BaseResultDto.error("扣款未完成，暂不能审核");
        }
        if (table.getApproveStatus() != 1) {
            return BaseResultDto.error("当前订单不是待审核状态");
        }
        if ("cancel".equals(table.getStatus())) {
            return BaseResultDto.error("订单已取消");
        }
        CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(table.getUniqueId());
        if (entity.getStatus() == 2) {
            // 审核通过，调用三方创建订单
            WasabiConfig config = getWasabiConfig();
            // 按照币种查询最新的货币汇率
            GlobalQueryExchangeRateEntity rateEntity = new GlobalQueryExchangeRateEntity();
            rateEntity.setPayoutCurrency(table.getPayoutCurrency());
            rateEntity.setTransferTypeId(table.getTransferType());
            rateEntity.setPayoutMethodId(table.getPayoutMethod());
            rateEntity.setUniqueId(table.getUniqueId());
            rateEntity.setAmount(table.getDeductionAmount());
            BigDecimal exchangeRate = getExchangeRateByCurrency(table.getPayoutCurrency(),
                    table.getTransferType(), table.getPayoutMethod());
            BigDecimal transferAmount = table.getDeductionAmount().multiply(exchangeRate).setScale(2,
                    RoundingMode.DOWN);
            table.setTransferAmount(transferAmount);
            WasabiTransactionTransferEntity transactionTransfer = new WasabiTransactionTransferEntity();
            BeanUtils.copyProperties(table, transactionTransfer);
            WasabiTransactionTransferDto transferDto = transactionTransfer(transactionTransfer, config);
            BeanUtils.copyProperties(transferDto, table);
            table.setApproveStatus(2);
            globalOrderRepository.save(table);
            List<WasabiRfiInfosDto> rfiInfos = transferDto.getRfiInfos();
            if (CollectionUtils.isNotEmpty(rfiInfos)) {
                rfiInfos.forEach(item -> {
                    WasabiRfiFieldInfoDto rfiFieldInfo = item.getRfiFieldInfo();
                    CardGlobalOrderDataTable dataTable = new CardGlobalOrderDataTable();
                    BeanUtils.copyProperties(item, dataTable);
                    BeanUtils.copyProperties(rfiFieldInfo, dataTable);
                    dataTable.setMerchantOrderNo(transferDto.getMerchantOrderNo());
                    globalOrderDataRepository.save(dataTable);
                    if (CollectionUtils.isNotEmpty(rfiFieldInfo.getSelectValues())) {
                        for (WasabiMethodDto selectValue : rfiFieldInfo.getSelectValues()) {
                            CardGlobalOrderDataAvailableTable availableTable = new CardGlobalOrderDataAvailableTable();
                            availableTable.setOrderNo(item.getOrderNo());
                            availableTable.setRfiId(item.getRfiId());
                            availableTable.setValueId(selectValue.getValueId());
                            availableTable.setValue(selectValue.getValue());
                            globalOrderDataAvailableRepository.save(availableTable);
                        }
                    }
                });
                // 发邮件给客户补充资料
                sendGlobalOrderSupplementaryDataEmail(userTable, table);
            }
            if (Constants.FAIL.equals(transferDto.getStatus())) {
                // 退款
                refundTransacOrder(table);
                // 交易失败 给客户发邮件
                sendGlobalOrderTransactionFailEmail(userTable, table);
            }
        } else if (entity.getStatus() == 3) {
            table.setApproveStatus(3);
            table.setApproveDesc(entity.getApproveDesc());
            table.setStatus(GlobalOrderStatusEnum.FAIL.getStatus());
            globalOrderRepository.save(table);
            // 退款
            refundTransacOrder(table);
            // 审核失败给用户发邮件
            sendGlobalOrderReviewFailEmail(userTable, Long.valueOf(entity.getApproveDesc()), table);
        }
        CardOperateTable operateTable = new CardOperateTable();
        operateTable.setUniqueId(table.getUniqueId());
        operateTable.setMerchantOrderNo(table.getMerchantOrderNo());
        operateTable.setOrderNo(table.getOrderNo());
        operateTable.setStatus(Constants.SUCCESS);
        operateTable.setOperateType(20);
        operateTable.setOperateUser(entity.getOperateUser());
        operateTable.setAddTime(new Date());
        cardOperateRepository.save(operateTable);
        return BaseResultDto.success();
    }

    /**
     * 速汇交易失败退款
     */
    @Transactional(rollbackFor = Exception.class)
    public void refundTransacOrder(CardGlobalOrderTable table) throws Exception {
        if ("1".equals(table.getDeductionAccountType())) {
            UcardCardTable cardTable = cardRepository.getByCardNumber(table.getCardNumber());
            WasabiConfig config = getWasabiConfig();
            WasabiBalanceEntity balanceEntity = new WasabiBalanceEntity();
            balanceEntity.setCardNo(cardTable.getCardNo());
            balanceEntity.setMerchantOrderNo(ObjectIdUtil.genSnowFlakeIdStr(getWasabiConfig().getMerchantTag()));
            balanceEntity.setAmount(table.getDeductionAmount().add(table.getDeductionFee()));

            TwoTuple<String, String> result =
                    WasabiCommonUtil.post(config.getApiUrl() + config.getCardDepositUrl(),
                            JSON.toJSONString(balanceEntity), config);
            verifySignature(result, config.getWsbPublicKey());
            ResultData<WasabiDepositDto> resultData = JSON.parseObject(result.getSecond(),
                    new TypeReference<ResultData<WasabiDepositDto>>() {
                    }
            );
            if (!resultData.isSuccess()) {
                log.error("速汇交易订单：{}退款失败:{}", table.getOrderNo(), resultData.getMsg());
            }

            WasabiDepositDto dto = resultData.getData();
            CardRechargeOrderTable rechargeTable = new CardRechargeOrderTable();
            BeanUtils.copyProperties(dto, rechargeTable);
            rechargeTable.setTransactionTime(DateUtil.formatTimestamp(dto.getTransactionTime()));
            rechargeTable.setAddTime(new Date());
            rechargeTable.setApproveStatus(2);
            rechargeTable.setCurrency("USD");
            rechargeTable.setReceivedCurrency("USD");
            rechargeTable.setRechargeType("2");
            rechargeTable.setCardNumber(table.getCardNumber());
            rechargeTable.setUniqueId(table.getUniqueId());
            rechargeTable.setRechargeFee(BigDecimal.ZERO);
            rechargeOrderRepository.save(rechargeTable);
            String status;
            if (Constants.SUCCESS.equals(dto.getStatus())) {
                status = "5";
            } else if (Constants.FAIL.equals(dto.getStatus())) {
                status = "6";
            } else {
                status = "4";
            }
            table.setDeductionAccountStatus(status);
            table.setRefundMerchantOrderNo(dto.getMerchantOrderNo());
            globalOrderRepository.save(table);
        } else if ("2".equals(table.getDeductionAccountType())) {
            UcardWalletTable walletTable = ucardWalletRepository.getFirstById(Long.valueOf(table.getCardNumber()));
            walletTable.setBalance(walletTable.getBalance().add(table.getDeductionAmount()).add(table.getDeductionFee()));
            ucardWalletRepository.save(walletTable);
            UcardWalletRecordTable walletRecordTable = new UcardWalletRecordTable();
            walletRecordTable.setWalletId(walletTable.getId());
            walletRecordTable.setUniqueId(walletTable.getUniqueId());
            walletRecordTable.setCId(walletTable.getCId());
            walletRecordTable.setAmount(table.getDeductionAmount());
            walletRecordTable.setFee(table.getDeductionFee());
            walletRecordTable.setSerial(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
            walletRecordTable.setStatus(2);
            walletRecordTable.setType(8);
            walletRecordTable.setAddTime(new Date());
            ucardWalletRecordRepository.save(walletRecordTable);
            table.setDeductionAccountStatus("5");
            globalOrderRepository.save(table);
        }
    }

    /**
     * 补充资料
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto supplementaryData(WasabiTransactionSubmitRfiEntity entity) throws Exception {
        String orderNo = entity.getOrderNo();
        CardGlobalOrderTable table = globalOrderRepository.findByOrderNo(orderNo);
        if (table == null) {
            return BaseResultDto.error(Constants.INFO_NOT_FOUND);
        }
        if (CollectionUtils.isEmpty(entity.getRfiInfos())) {
            return BaseResultDto.error(Constants.PARAMETER_ERROR);
        }
        // 处理文件 上传到三方
        for (WasabiTransactionSubmitRfiInfosEntity item : entity.getRfiInfos()) {
            if (StringUtils.isNotEmpty(item.getFieldType()) && "file".equals(item.getFieldType())) {
                String rfiValue = uploadFile(item.getRfiValueUrl());
                item.setRfiValue(rfiValue);
            }
        }
        transactionSubmitRfi(entity);
        table.setComplianceStatus(GlobalComplianceStatusEnum.PENDING.getStatus());
        globalOrderRepository.save(table);
        // 更新数据库字段信息
        for (WasabiTransactionSubmitRfiInfosEntity item : entity.getRfiInfos()) {
            CardGlobalOrderDataTable orderDataTable = globalOrderDataRepository.findByOrderNoAndRfiId(orderNo,
                    item.getRfiId());
            if (orderDataTable != null) {
                orderDataTable.setStatus(GlobalComplianceStatusEnum.PENDING.getStatus());
                orderDataTable.setRfiValue(item.getRfiValue());
                orderDataTable.setRfiValueUrl(item.getRfiValueUrl());
                globalOrderDataRepository.save(orderDataTable);
            }
        }
        CardOperateTable operateTable = new CardOperateTable();
        operateTable.setUniqueId(table.getUniqueId());
        operateTable.setMerchantOrderNo(table.getMerchantOrderNo());
        operateTable.setOrderNo(table.getOrderNo());
        operateTable.setStatus(Constants.SUCCESS);
        operateTable.setOperateType(22);
        operateTable.setOperateUser(entity.getCId());
        operateTable.setAddTime(new Date());
        cardOperateRepository.save(operateTable);
        return BaseResultDto.success();
    }

    /**
     * 查询认证成功用户列表
     */
    @Override
    public BaseResultDto<List<MerchantUserDto>> queryUcardUserList() throws ServiceException {
        List<MerchantUserDto> dtos = merchantUserMapper.findAllByAuthSuccess();
        return BaseResultDto.success(dtos);
    }

    /**
     * 区块链配置列表
     */
    @Override
    public ResultWithPagerDto<CardBlockchainConfigDto> getCardBlockchainConfigPageList(CardBlockchainConfigPageEntity entity) throws ServiceException {
        try {
            // 获取总数
            Integer count = blockchainConfigMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }
            List<CardBlockchainConfigDto> dtos = blockchainConfigMapper.pageList(entity);
            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("获取区块链配置列表失败", e);
            throw new ServiceException("获取区块链配置列表失败");
        }
    }

    /**
     * 区块链配置下拉列表
     */
    @Override
    public BaseResultDto getCardBlockchainConfigDropdown(CardBlockchainConfigDropdownEntity entity) throws ServiceException {
        List<CardBlockchainConfigDto> dtos = new ArrayList<>();
        List<CardBlockchainConfigTable> tables = blockchainConfigRepository.findAllByEnableStatus(1);
        if (CollectionUtils.isNotEmpty(tables)) {
            for (CardBlockchainConfigTable table : tables) {
                boolean shouldAdd = false;

                // 判断白名单
                if (StringUtils.isNotEmpty(table.getWhitelist())) {
                    // 白名单不为空，检查入参id是否在白名单中
                    List<String> whitelist;
                    if (table.getWhitelist().contains(",")) {
                        // 包含逗号，按逗号分割
                        whitelist = Arrays.asList(table.getWhitelist().split(","));
                    } else {
                        // 不包含逗号，只有一个用户ID
                        whitelist = Collections.singletonList(table.getWhitelist());
                    }
                    if (whitelist.contains(entity.getUniqueId())) {
                        shouldAdd = true;
                    }
                } else {
                    // 白名单为空，检查黑名单
                    if (StringUtils.isNotEmpty(table.getBlacklist())) {
                        // 黑名单不为空，检查入参id是否在黑名单中
                        List<String> blacklist;
                        if (table.getBlacklist().contains(",")) {
                            // 包含逗号，按逗号分割
                            blacklist = Arrays.asList(table.getBlacklist().split(","));
                        } else {
                            // 不包含逗号，只有一个用户ID
                            blacklist = Collections.singletonList(table.getBlacklist());
                        }
                        if (!blacklist.contains(entity.getUniqueId())) {
                            shouldAdd = true;
                        }
                    } else {
                        // 黑名单也为空，添加该数据
                        shouldAdd = true;
                    }
                }

                // 如果符合条件，添加到结果中
                if (shouldAdd) {
                    CardBlockchainConfigDto dto = new CardBlockchainConfigDto();
                    BeanUtils.copyProperties(table, dto);
                    dtos.add(dto);
                }
            }
        }
        return BaseResultDto.success(dtos);
    }

    /**
     * 区块链下拉列表
     */
    @Override
    public BaseResultDto<List<String>> getCardBlockchainDropdown() throws ServiceException {
        List<String> list = CardBlockchainEnum.getAllBlockchains();
        return BaseResultDto.success(list);
    }

    /**
     * 新增区块链配置
     */
    @Override
    public BaseResultDto addCardBlockchainConfig(CardBlockchainConfigAddEntity entity) throws ServiceException {
        if (StringUtils.isNotEmpty(entity.getWhitelist()) && StringUtils.isNotEmpty(entity.getBlacklist())) {
            throw ServiceException.exception("白名单和黑名单不能同时存在");
        }
        blockchainConfigMapper.insert(entity);
        return BaseResultDto.success();
    }

    /**
     * 修改区块链配置
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto updateCardBlockchainConfig(CardBlockchainConfigUpdateEntity entity) throws ServiceException {
        if (StringUtils.isNotEmpty(entity.getWhitelist()) && StringUtils.isNotEmpty(entity.getBlacklist())) {
            throw ServiceException.exception("白名单和黑名单不能同时存在");
        }
        CardBlockchainConfigTable table = blockchainConfigRepository.findFirstById(entity.getId());
        if (table == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        BeanUtils.copyProperties(entity, table);
        blockchainConfigRepository.save(table);
        return BaseResultDto.success();
    }

    /**
     * 批量删除区块链配置
     */
    @Override
    public BaseResultDto deleteCardBlockchainConfig(BaseDeleteEntities entity) throws ServiceException {
        List<CardBlockchainConfigTable> tables = Lists.newArrayList();
        for (Long id : entity.getIds()) {
            CardBlockchainConfigTable table = new CardBlockchainConfigTable();
            table.setId(id);
            tables.add(table);
        }
        blockchainConfigRepository.deleteInBatch(tables);
        return BaseResultDto.success();
    }

    /**
     * 加密货币交易分页列表
     */
    @Override
    public ResultWithPagerDto<EncryptedWalletTransactionDto> getEncryptedWalletTransactionPage(EncryptedWalletTransactionPageEntity entity) throws ServiceException {
        try {
            // 获取总数
            Integer count = cryptoCallbackRecordMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }

            List<EncryptedWalletTransactionDto> dtos = cryptoCallbackRecordMapper.pageList(entity);
            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("获取加密货币交易分页列表失败", e);
            throw new ServiceException("获取加密货币交易分页列表失败");
        }
    }

    /**
     * 加密货币交易分页列表导出
     */
    @Override
    public BaseResultDto<List<EncryptedWalletTransactionDto>> encryptedWalletTransactionListExport(EncryptedWalletTransactionPageEntity entity) throws ServiceException {
        entity.setPage(null);
        List<EncryptedWalletTransactionDto> dtos = cryptoCallbackRecordMapper.pageList(entity);
        return BaseResultDto.success(dtos);

    }

    /**
     * 区块链汇率配置列表
     */
    @Override
    public ResultWithPagerDto<CardBlockchainRateConfigDto> getCardBlockchainRateConfigPageList(CardBlockchainRateConfigPageEntity entity) throws ServiceException {
        try {
            // 获取总数
            Integer count = blockchainRateConfigMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }
            List<CardBlockchainRateConfigDto> dtos = blockchainRateConfigMapper.pageList(entity);
            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("获取区块链配置列表失败", e);
            throw new ServiceException("获取区块链配置列表失败");
        }
    }

    /**
     * 新增区块链汇率配置
     */
    @Override
    public BaseResultDto addCardBlockchainRateConfig(CardBlockchainRateConfigAddEntity entity) throws ServiceException {
        blockchainRateConfigMapper.insert(entity);
        return BaseResultDto.success();
    }

    /**
     * 修改区块链汇率配置
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto updateCardBlockchainRateConfig(CardBlockchainRateConfigUpdateEntity entity) throws ServiceException {
        CardBlockchainRateConfigTable table = blockchainRateConfigRepository.findFirstById(entity.getId());
        if (table == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        BeanUtils.copyProperties(entity, table);
        blockchainRateConfigRepository.save(table);
        return BaseResultDto.success();
    }

    /**
     * 批量删除区块链汇率配置
     */
    @Override
    public BaseResultDto deleteCardBlockchainRateConfig(BaseDeleteEntities entity) throws ServiceException {
        List<CardBlockchainRateConfigTable> tables = Lists.newArrayList();
        for (Long id : entity.getIds()) {
            CardBlockchainRateConfigTable table = new CardBlockchainRateConfigTable();
            table.setId(id);
            tables.add(table);
        }
        blockchainRateConfigRepository.deleteInBatch(tables);
        return BaseResultDto.success();
    }


    /**
     * 文件上传
     */
    @Override
    public String commonUpload(MultipartFile file, String middlePath) throws Exception {
        if (!Strings.isNullOrEmpty(middlePath) && !middlePath.startsWith("/")) {
            middlePath = "/" + middlePath;
        }
        try {
            return fIleFeignService.commonUpload(file, Base64Util.toBase64String(middlePath));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 最近交易收款人列表
     */
    @Override
    public BaseResultDto<List<GlobalReceiverUserDto>> getGlobalReceiverUserList(GlobalReceiverUserEntity entity) throws ServiceException {
        List<GlobalReceiverUserDto> dtos = new ArrayList<>();
        List<CardGlobalReceiverInfoTable> tables = globalReceiverInfoRepository.findAllByUniqueId(entity.getUniqueId());
        if (CollectionUtils.isNotEmpty(tables)) {
            for (CardGlobalReceiverInfoTable table : tables) {
                GlobalReceiverUserDto dto = new GlobalReceiverUserDto();
                BeanUtils.copyProperties(table, dto);
                dtos.add(dto);
            }
        }
        return BaseResultDto.success(dtos);
    }

    /**
     * 删除最近交易收款人信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto deleteGlobalReceiverUser(BaseEntity entity) throws ServiceException {
        CardGlobalReceiverInfoTable table = globalReceiverInfoRepository.findFirstById(entity.getId());
        if (table != null) {
            globalReceiverInfoRepository.delete(table);
        }
        return BaseResultDto.success();
    }

    /**
     * 查询cvv发送验证码邮件
     */
    private SysEmailSendEntity createQueryCvvSendEmailCodeSysEmailEntity(CardQueryCvvSendEmailCodeEntity entity) {
        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();

        Map<String, String> map = new HashMap<>(4);
        String code = GetRandom.getRandom(6);
        map.put(MapConstants.CODE, code);
        Date time = DateUtil.operationMinute(new Date(), Constants.EMAIL_REGISTER_VALIDATECODE_TIME);
        map.put(MapConstants.EXPIRE_TIME, DateUtil.formatTime(time));
        map.put(MapConstants.DATE_TIME, DateUtil.formatTime());

        sysEmailSendEntity.setAddIp(entity.getIp());
        sysEmailSendEntity.setAddTime(entity.getTime());

        sysEmailSendEntity.setUsers(entity.getEmail());
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.QUERY_CVV_CODE);
        if (CountryUtil.isCN(entity.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.CARD_QUERY_CVV_SEND_EMAIL_CODE_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.CARD_QUERY_CVV_SEND_EMAIL_CODE_EN);
        }
        sysEmailSendEntity.setMap(map);

        return sysEmailSendEntity;
    }

    /**
     * 验证金额小数位数不能超过10位
     *
     * @param amount    待验证的金额
     * @param fieldName 字段名称（用于错误提示）
     * @throws ServiceException 当小数位数超过10位时抛出异常
     */
    private void validateDecimalPlaces(BigDecimal amount, String fieldName, int maxDecimalPlaces) throws ServiceException {
        if (amount != null) {
            String amountStr = amount.stripTrailingZeros().toPlainString();
            int decimalIndex = amountStr.indexOf(".");
            if (decimalIndex != -1) {
                int decimalPlaces = amountStr.length() - decimalIndex - 1;
                if (decimalPlaces > maxDecimalPlaces) {
                    throw new ServiceException(fieldName + "小数位数不能超过" + maxDecimalPlaces + "位");
                }
            }
        }
    }

    private void existByEmail(String email) throws ServiceException {
        boolean existByEmail = merchantUserRepository.existsByEmail(email);
        if (!existByEmail) {
            throw new ServiceException(Constants.EMAIL_IS_EXIST_ERROR);
        }
    }

    private void pushMessage(CardWebSocketEntity socketEntity) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String token = null;
        if (null != attributes) {
            HttpServletRequest request = attributes.getRequest();
            token = request.getHeader(Constants.ACCESS_TOKEN);
        }
        log.info("推送websocket token：{}", token);
        if (StringUtils.isEmpty(token)) {
            return;
        }
        WebsocketPushMessageByCustomIdEntity entity = new WebsocketPushMessageByCustomIdEntity();
        entity.setCustomId(socketEntity.getCustomId());
        entity.setData(socketEntity.getData());
        try {
            log.info("推送websocket entity：{}", JSON.toJSONString(entity));
            pushMessageFeignService.pushMessageUcardCustomIdNotice(entity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 用户信息审核成功发送邮件
     */
    private void sendUserInfoReviewSuccessEmail(CardMerchantUserTable userTable) throws ServiceException {
        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(2);

        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG USER INFO REVIEW SUCCESS");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.USER_INFO_REVIEW_SUCCESS);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.USER_INFO_REVIEW_SUCCESS_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.USER_INFO_REVIEW_SUCCESS_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 用户信息审核失败发送邮件
     */
    private void sendUserInfoReviewFailEmail(CardMerchantUserTable userTable, Long reason) throws ServiceException {
        String reasonsRefusal = "";
        if (reason != null) {
            SysReasonsRefusalTable rejectTable = sysReasonsRefusalRepository.getFirstById(reason);
            if (rejectTable != null) {
                if (CountryUtil.isCN(userTable.getCountry())) {
                    reasonsRefusal = rejectTable.getContent();
                } else {
                    reasonsRefusal = rejectTable.getEnContent();
                }
            }
        }
        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(3);
        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.REASONS_REFUSAL, reasonsRefusal);

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG USER INFO REVIEW FAILURE");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.USER_INFO_REVIEW_FAIL);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.USER_INFO_REVIEW_FAIL_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.USER_INFO_REVIEW_FAIL_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 开卡申请发送邮件通知审核人员审核
     */
    private void sendApplyCardReviewEmail(CardApplyTable applyTable) throws ServiceException {
        CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(applyTable.getUniqueId());

        SysConfigTable emailConfigTable = sysConfigService.getByCode(ConfigConstants.APPLY_CARD_REVIEW_EMAIL);
        if (emailConfigTable == null) {
            throw ServiceException.exception(Constants.SYSTEM_ERROR);
        }
        //构建map 用于后面邮件模版替换
        Map<String, String> map = new HashMap<>(7);
        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.EMAIL, userTable.getEmail());
        map.put(MapConstants.V_FIRST_NAME_V, userTable.getFirstName());
        map.put(MapConstants.V_LAST_NAME_V, userTable.getLastName());
        map.put(MapConstants.C_ID, String.valueOf(userTable.getCId()));
        map.put(MapConstants.PHONE, userTable.getMobile());
        map.put(MapConstants.APPLY_TIME, DateUtil.formatTime(applyTable.getAddTime()));

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG CARD APPLY CARD REVIEW");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.APPLY_CARD_REVIEW);
        sysEmailSendEntity.setMap(map);
        sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.APPLY_CARD_REVIEW);
        sysEmailSendEntity.setUsers(emailConfigTable.getValue());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 开卡成功发送邮件
     */
    private void sendOpenCardSuccessEmail(UcardCardTable cardTable) throws ServiceException {
        CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(cardTable.getUniqueId());
        // 实体卡卡号脱敏
        String cardNumber = MaskUtil.mask(cardTable.getCardNumber(), 6, 4);

        // 查询激活码
        WasabiThird3dsTransactionEntity entity = new WasabiThird3dsTransactionEntity();
        entity.setCId(null);
        entity.setCardNo(cardTable.getCardNo());
        String activationCode = null;
        try {
            BaseResultDto<WasabiThird3dsTransactionDto> dto = getActivationCode(entity);
            activationCode = dto.getData().getValues();
        } catch (Exception e) {
            log.error("开卡成功发送邮件 - 查询卡激活码失败", e);
        }
        if (activationCode == null) {
            return;
        }

        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(4);

        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.ACTIVATION_CODE, activationCode);
        map.put(MapConstants.CARD_NUMBER, cardNumber);

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG CARD OPEN CARD SUCCESS");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.OPEN_CARD_SUCCESS);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.OPEN_CARD_SUCCESS_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.OPEN_CARD_SUCCESS_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 开卡审批失败发送邮件
     */
    private void sendOpenCardApprovalFailEmail(CardMerchantUserTable userTable, Long reason) throws ServiceException {
        String reasonsRefusal = "";
        if (reason != null) {
            SysReasonsRefusalTable rejectTable = sysReasonsRefusalRepository.getFirstById(reason);
            if (rejectTable != null) {
                if (CountryUtil.isCN(userTable.getCountry())) {
                    reasonsRefusal = rejectTable.getContent();
                } else {
                    reasonsRefusal = rejectTable.getEnContent();
                }
            }
        }
        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(3);
        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.REASONS_REFUSAL, reasonsRefusal);

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG CARD OPEN CARD APPROVAL FAIL");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.OPEN_CARD_FAIL);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.OPEN_CARD_FAIL_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.OPEN_CARD_FAIL_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 激活成功发送邮件
     */
    private void sendActivateCardSuccessEmail(UcardCardTable cardTable) throws ServiceException {
        CardMerchantUserTable userTable = merchantUserRepository.getByUniqueId(cardTable.getUniqueId());
        String cardNumber = MaskUtil.mask(cardTable.getCardNumber(), 6, 4);
        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(3);

        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.CARD_NUMBER, cardNumber);

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG CARD ACTIVATE CARD SUCCESS");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.ACTIVATE_CARD_SUCCESS);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.ACTIVATE_CARD_SUCCESS_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.ACTIVATE_CARD_SUCCESS_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 充值申请给财务发邮件
     */
    private void sendRechargeReviewEmail(CardMerchantUserTable userTable, String cardNumber, BigDecimal amount,
                                         BigDecimal fee, Date applyTime) throws ServiceException {

        SysConfigTable emailConfigTable = sysConfigService.getByCode(ConfigConstants.RECHARGE_CARD_REVIEW_EMAIL);
        if (emailConfigTable == null) {
            throw ServiceException.exception(Constants.SYSTEM_ERROR);
        }
        //构建map 用于后面邮件模版替换
        Map<String, String> map = new HashMap<>(10);
        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.C_ID, String.valueOf(userTable.getCId()));
        map.put(MapConstants.V_FIRST_NAME_V, userTable.getFirstName());
        map.put(MapConstants.V_LAST_NAME_V, userTable.getLastName());
        map.put(MapConstants.EMAIL, userTable.getEmail());
        map.put(MapConstants.PHONE, userTable.getMobile());
        map.put(MapConstants.CARD_NUMBER, String.valueOf(cardNumber));
        map.put(MapConstants.AMOUNT, String.valueOf(amount));
        map.put(MapConstants.FEE, String.valueOf(fee));
        map.put(MapConstants.APPLY_TIME, DateUtil.formatTime(applyTime));

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG CARD RECHARGE REVIEW");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.RECHARGE_CARD_REVIEW);
        sysEmailSendEntity.setMap(map);
        sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.RECHARGE_CARD_REVIEW);
        sysEmailSendEntity.setUsers(emailConfigTable.getValue());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 充值成功发送邮件
     */
    private void sendCardRechargeSuccessEmail(CardMerchantUserTable userTable,
                                              CardRechargeOrderTable orderTable) throws ServiceException {
        String number = MaskUtil.mask(orderTable.getCardNumber(), 6, 4);

        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(9);

        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.CARD_NUMBER, number);
        map.put(MapConstants.AMOUNT, String.valueOf(orderTable.getAmount()));
        map.put(MapConstants.FEE, String.valueOf(orderTable.getFee()));
        map.put(MapConstants.CURRENCY_TYPE, orderTable.getCurrency());
        map.put(MapConstants.RECEIVED_AMOUNT, String.valueOf(orderTable.getReceivedAmount()));
        map.put(MapConstants.RECEIVED_CURRENCY, orderTable.getReceivedCurrency());
        map.put(MapConstants.SUCCESS_TIME, orderTable.getTransactionTime());

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG CARD RECHARGE SUCCESS");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.CARD_RECHARGE_SUCCESS);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.CARD_RECHARGE_SUCCESS_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.CARD_RECHARGE_SUCCESS_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 充值审核失败发送邮件
     */
    private void sendCardRechargeReviewFailEmail(CardMerchantUserTable userTable,
                                                 CardRechargeOrderTable orderTable,
                                                 Long reason) throws ServiceException {
        String reasonsRefusal = "";
        if (reason != null) {
            SysReasonsRefusalTable rejectTable = sysReasonsRefusalRepository.getFirstById(reason);
            if (rejectTable != null) {
                if (CountryUtil.isCN(userTable.getCountry())) {
                    reasonsRefusal = rejectTable.getContent();
                } else {
                    reasonsRefusal = rejectTable.getEnContent();
                }
            }
        }
        String number = MaskUtil.mask(orderTable.getCardNumber(), 6, 4);

        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(8);
        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.CARD_NUMBER, number);
        map.put(MapConstants.AMOUNT, String.valueOf(orderTable.getAmount()));
        map.put(MapConstants.FEE, String.valueOf(orderTable.getFee()));
        map.put(MapConstants.CURRENCY_TYPE, orderTable.getCurrency());
        map.put(MapConstants.APPLY_TIME, DateUtil.formatTime(orderTable.getAddTime()));
        map.put(MapConstants.REASONS_REFUSAL, reasonsRefusal);

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG CARD RECHARGE REVIEW FAIL");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.CARD_RECHARGE_REVIEW_FAIL);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.CARD_RECHARGE_FAIL_REVIEW_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.CARD_RECHARGE_FAIL_REVIEW_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 修改密码成功发送邮件
     */
    private void sendCardUpdatePasswordSuccessEmail(CardMerchantUserTable userTable, String cardNumber) throws ServiceException {
        String number = MaskUtil.mask(cardNumber, 6, 4);
        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(3);

        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.CARD_NUMBER, number);

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG CARD UPDATE PASSWORD SUCCESS");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.CARD_PASSWORD_UPDATE_SUCCESS);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.CARD_PASSWORD_UPDATE_SUCCESS_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.CARD_PASSWORD_UPDATE_SUCCESS_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 用户上传证件照片邮件
     */
    private void sendUserUploadPhotoEmail(CardMerchantUserTable userTable) throws ServiceException {
        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(2);

        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG USER UPLOAD PHOTO");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.USER_UPLOAD_PHOTO);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.USER_UPLOAD_PHOTO_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.USER_UPLOAD_PHOTO_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 激活码通知邮件
     */
    private void sendUserActivationCodeEmail(CardMerchantUserTable userTable, String cardNumber,
                                             String activationCode) throws ServiceException {
        String number = MaskUtil.mask(cardNumber, 6, 4);
        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(4);

        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.ACTIVATION_CODE, activationCode);
        map.put(MapConstants.CARD_NUMBER, number);

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG CARD ACTIVATION CODE");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.ACTIVATION_CODE);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.ACTIVATION_CODE_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.ACTIVATION_CODE_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 3ds通知邮件
     */
    private void sendUser3dsEmail(CardMerchantUserTable userTable, String cardNumber, String activationCode,
                                  WasabiThird3dsTransactionDto dto) throws ServiceException {
        String number = MaskUtil.mask(cardNumber, 6, 4);
        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(6);

        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.ACTIVATION_CODE, activationCode);
        map.put(MapConstants.CARD_NUMBER, number);
        map.put(MapConstants.MERCHANT_NAME, dto.getMerchantName());
        map.put(MapConstants.EXPIRE_TIME, DateUtil.formatTimestamp(dto.getExpirationTime()));
        // 一次性验证码

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG CARD 3DS NOTICE");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.CARD_THIRD_3DS_OTP);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.CARD_THIRD_3DS_OTP_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.CARD_THIRD_3DS_OTP_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 交易授权url通知邮件
     */
    private void sendUserAuthUrlEmail(CardMerchantUserTable userTable, String cardNumber, String activationCode,
                                      WasabiThird3dsTransactionDto dto) throws ServiceException {
        String number = MaskUtil.mask(cardNumber, 6, 4);
        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(6);

        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.ACTIVATION_CODE, activationCode);
        map.put(MapConstants.CARD_NUMBER, number);
        map.put(MapConstants.MERCHANT_NAME, dto.getMerchantName());
        map.put(MapConstants.EXPIRE_TIME, DateUtil.formatTimestamp(dto.getExpirationTime()));
        // 一次性验证码

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG CARD AUTH URL");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.CARD_AUTH_URL);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.CARD_AUTH_URL_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.CARD_AUTH_URL_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 速汇订单审核邮件
     */
    private void sendGlobalOrderReviewEmail(CardMerchantUserTable userTable, CardGlobalOrderTable orderTable) throws ServiceException {
        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        SysConfigTable emailConfigTable = sysConfigService.getByCode(ConfigConstants.GLOBAL_ORDER_REVIEW_EMAIL);
        if (emailConfigTable == null) {
            throw ServiceException.exception(Constants.SYSTEM_ERROR);
        }
        Map<String, String> map = new HashMap<>(9);

        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.EMAIL, userTable.getEmail());
        map.put(MapConstants.PHONE, userTable.getMobile());
        map.put(MapConstants.AMOUNT, String.valueOf(orderTable.getDeductionAmount()));
        map.put(MapConstants.C_ID, String.valueOf(userTable.getCId()));
        map.put(MapConstants.FEE, String.valueOf(orderTable.getDeductionFee()));
        map.put(MapConstants.APPLY_TIME, DateUtil.formatTime(orderTable.getAddTime()));
        map.put(MapConstants.ORDER_NUMBER, orderTable.getMerchantOrderNo());

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("CWG GLOBAL ORDER REVIEW");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.GLOBAL_ORDER_REVIEW);
        sysEmailSendEntity.setMap(map);
        sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.GLOBAL_ORDER_REVIEW);
        sysEmailSendEntity.setUsers(emailConfigTable.getValue());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 速汇订单审核失败发送邮件
     */
    private void sendGlobalOrderReviewFailEmail(CardMerchantUserTable userTable, Long reason,
                                                CardGlobalOrderTable orderTable) throws ServiceException {
        String reasonsRefusal = "";
        if (reason != null) {
            SysReasonsRefusalTable rejectTable = sysReasonsRefusalRepository.getFirstById(reason);
            if (rejectTable != null) {
                if (CountryUtil.isCN(userTable.getCountry())) {
                    reasonsRefusal = rejectTable.getContent();
                } else {
                    reasonsRefusal = rejectTable.getEnContent();
                }
            }
        }
        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(10);
        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.REASONS_REFUSAL, reasonsRefusal);
        map.put(MapConstants.ORDER_NUMBER, orderTable.getMerchantOrderNo());
        map.put(MapConstants.PAYOUT_CURRENCY, orderTable.getPayoutCurrency());
        map.put(MapConstants.TRANSFER_TYPE, orderTable.getTransferType());
        map.put(MapConstants.PAYOUT_METHOD, orderTable.getPayoutMethod());
        map.put(MapConstants.AMOUNT, String.valueOf(orderTable.getDeductionAmount()));
        map.put(MapConstants.FEE, String.valueOf(orderTable.getDeductionFee()));
        map.put(MapConstants.APPLY_TIME, DateUtil.formatTime(orderTable.getAddTime()));

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("GLOBAL ORDER REVIEW FAIL");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.GLOBAL_ORDER_REVIEW_FAIL);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.GLOBAL_ORDER_REVIEW_FAIL_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.GLOBAL_ORDER_REVIEW_FAIL_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 速汇订单补充资料发送邮件
     */
    private void sendGlobalOrderSupplementaryDataEmail(CardMerchantUserTable userTable,
                                                       CardGlobalOrderTable orderTable) throws ServiceException {

        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(3);
        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.ORDER_NUMBER, orderTable.getMerchantOrderNo());

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("GLOBAL ORDER SUPPLEMENTARY DATA");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.GLOBAL_ORDER_SUPPLEMENTARY_DATA);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.GLOBAL_ORDER_SUPPLEMENTARY_DATA_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.GLOBAL_ORDER_SUPPLEMENTARY_DATA_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 速汇订单取消订单发送邮件
     */
    private void sendGlobalOrderCancelEmail(CardMerchantUserTable userTable, CardGlobalOrderTable orderTable) throws ServiceException {

        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(3);
        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.ORDER_NUMBER, orderTable.getMerchantOrderNo());

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("GLOBAL ORDER CANCEL");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.GLOBAL_ORDER_CANCEL);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.GLOBAL_ORDER_CANCEL_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.GLOBAL_ORDER_CANCEL_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 速汇订单交易失败发送邮件
     */
    private void sendGlobalOrderTransactionFailEmail(CardMerchantUserTable userTable,
                                                     CardGlobalOrderTable orderTable) throws ServiceException {

        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(10);
        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.REASONS_REFUSAL, orderTable.getError());
        map.put(MapConstants.ORDER_NUMBER, orderTable.getMerchantOrderNo());
        map.put(MapConstants.PAYOUT_CURRENCY, orderTable.getPayoutCurrency());
        map.put(MapConstants.TRANSFER_TYPE, orderTable.getTransferType());
        map.put(MapConstants.PAYOUT_METHOD, orderTable.getPayoutMethod());
        map.put(MapConstants.AMOUNT, String.valueOf(orderTable.getDeductionAmount()));
        map.put(MapConstants.FEE, String.valueOf(orderTable.getDeductionFee()));
        map.put(MapConstants.APPLY_TIME, DateUtil.formatTime(orderTable.getAddTime()));

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("GLOBAL ORDER TRANSACTION FAIL");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.GLOBAL_ORDER_TRANSACTION_FAIL);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.GLOBAL_ORDER_TRANSACTION_FAIL_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.GLOBAL_ORDER_TRANSACTION_FAIL_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 速汇订单交易成功发送邮件
     */
    private void sendGlobalOrderTransactionSuccessEmail(CardMerchantUserTable userTable,
                                                        CardGlobalOrderTable orderTable) throws ServiceException {

        //构建map 用于后面邮件模版替换
        String name = CustomInfoUtil.getName(userTable.getFirstName(), null, userTable.getLastName());
        Map<String, String> map = new HashMap<>(10);
        map.put(MapConstants.DATE_TIME, DateUtil.formatTime(new Date()));
        map.put(MapConstants.NAME, name);
        map.put(MapConstants.REASONS_REFUSAL, orderTable.getError());
        map.put(MapConstants.ORDER_NUMBER, orderTable.getMerchantOrderNo());
        map.put(MapConstants.PAYOUT_CURRENCY, orderTable.getPayoutCurrency());
        map.put(MapConstants.TRANSFER_TYPE, orderTable.getTransferType());
        map.put(MapConstants.PAYOUT_METHOD, orderTable.getPayoutMethod());
        map.put(MapConstants.AMOUNT, String.valueOf(orderTable.getDeductionAmount()));
        map.put(MapConstants.FEE, String.valueOf(orderTable.getDeductionFee()));
        map.put(MapConstants.APPLY_TIME, DateUtil.formatTime(orderTable.getAddTime()));

        SysEmailSendEntity sysEmailSendEntity = new SysEmailSendEntity();
        sysEmailSendEntity.setSubject("GLOBAL ORDER TRANSACTION SUCCESS");
        sysEmailSendEntity.setEmailSendEnum(EmailSendEnum.GLOBAL_ORDER_TRANSACTION_SUCCESS);
        sysEmailSendEntity.setMap(map);
        if (CountryUtil.isCN(userTable.getCountry())) {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.GLOBAL_ORDER_TRANSACTION_SUCCESS_CN);
        } else {
            sysEmailSendEntity.setTemplateName(SystemEmailCodeConstants.GLOBAL_ORDER_TRANSACTION_SUCCESS_EN);
        }
        sysEmailSendEntity.setUsers(userTable.getEmail());
        try {
            emailService.sendEmail(sysEmailSendEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 同步卡授权交易记录任务
     */
    @Override
    public void queryOrderInfo() throws ServiceException {
        WasabiConfig config = getWasabiConfig();

        try {
            WasabiAuthTransactionEntity entity = new WasabiAuthTransactionEntity();
            // 查询时间为前一天
            LocalDate yesterday = LocalDate.now().minusDays(1);
            LocalDateTime yesterdayStart = yesterday.atStartOfDay();
            LocalDateTime yesterdayEnd = yesterday.atTime(LocalTime.MAX);

            entity.setStartTime(yesterdayStart.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
            entity.setEndTime(yesterdayEnd.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());

            // 获取到银行卡信息
            List<UcardCardTable> cardlist = cardRepository.findAll();
            if (cardlist.isEmpty()) {
                return;
            }

            // 批量查询本地已存在的交易记录
            List<String> allCardNos = cardlist.stream().map(UcardCardTable::getCardNo).collect(Collectors.toList());
            List<CardTransacOrderTable> localOrders = cardTransacOrderRepository.findByCardNoIn(allCardNos);
            Set<String> localTradeNos = localOrders.stream()
                    .map(CardTransacOrderTable::getTradeNo)
                    .collect(Collectors.toSet());

            List<CardTransacOrderTable> tablesToSave = new ArrayList<>();
            for (UcardCardTable table : cardlist) {
                String cardNo = table.getCardNo();

                int page = 1;
                while (true) {
                    entity.setCardNo(cardNo);
                    entity.setPageNum((long) page);
                    entity.setPageSize(30L);
                    TwoTuple<String, String> result =
                            WasabiCommonUtil.post(config.getApiUrl() + config.getCardAuthTransactionUrl(),
                                    JSON.toJSONString(entity), config);
                    verifySignature(result, config.getWsbPublicKey());
                    ResultData resultData = JSON.parseObject(result.getSecond(), ResultData.class);
                    if (!resultData.isSuccess()) {
                        log.error("同步卡授权交易记录任务异常：cardNo:{},message:{}", cardNo, resultData.getMsg());
                        break;
                    }
                    JSONObject data = JSON.parseObject(resultData.getData().toString());
                    Integer total = data.getInteger("total");

                    if (total == null || total <= 0) {
                        break;
                    }
                    JSONArray records = data.getJSONArray("records");
                    List<WasabiAuthTransactionDto> dtos = records.toJavaList(WasabiAuthTransactionDto.class);

                    //比对数据
                    for (WasabiAuthTransactionDto dto : dtos) {
                        if (!localTradeNos.contains(dto.getTradeNo())) {
                            CardTransacOrderTable saveTable = new CardTransacOrderTable();
                            BeanUtils.copyProperties(dto, saveTable);
                            WasabiMerchantDataDto merchantDataDto = dto.getMerchantData();
                            BeanUtils.copyProperties(merchantDataDto, saveTable);
                            saveTable.setAddTime(new Date());
                            tablesToSave.add(saveTable);
                        }
                    }

                    if (dtos.size() < 30) {
                        break;
                    }
                    page++;
                }
            }

            if (!tablesToSave.isEmpty()) {
                cardTransacOrderRepository.save(tablesToSave);
                log.info("本次同步新增 {} 条交易记录", tablesToSave.size());
            } else {
                log.info("本次同步无新增交易记录");
            }
        } catch (Exception e) {
            log.error("银行卡授权交易信息数据定时同步异常", e);
            throw new ServiceException("银行卡授权交易信息数据定时同步失败，请稍后重试");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void queryCardInfo() throws Exception {
        // 查询卡激活处理中数据
        List<UcardCardTable> cardTables = cardRepository.findAllByStatus("processing");
        if (CollectionUtils.isEmpty(cardTables)) {
            return;
        }
        for (UcardCardTable table : cardTables) {
            log.info("激活中数据同步定时任务,开始处理-cardNumber:{}", table.getCardNumber());
            activateCallback(JSON.toJSONString(table));
        }
    }

    /**
     * 卡邮寄状态同步
     */
    @Override
    public void queryCardLogisticsStatus() throws Exception {
        // 查询待激活并且物流状态不为终态的数据
        List<UcardCardTable> tables =
                cardRepository.findAllByStatusAndLogisticsStatusNot(CardStatusTypeEnum.UN_ACTIVATE.getCode(), "4");
        if (CollectionUtils.isNotEmpty(tables)) {
            for (UcardCardTable table : tables) {
                mqSendService.send(Constants.CARD_LOGISTICS_STATUS_UPDATE, JSONObject.toJSONString(table));
            }
        }
    }


    public boolean matchData(CardPermissionTable table, CustomInfoEntity customInfo) {
        boolean match;
        if (StringUtils.isNotEmpty(table.getCIdRegexp())) {
            match = Pattern.matches(table.getCIdRegexp(), customInfo.getCId().toString());
        } else {
            match = true;
        }
        if (match && StringUtils.isNotEmpty(table.getEmailRegexp())) {
            match = Pattern.matches(table.getEmailRegexp(), customInfo.getEmail());
        }
        if (match && StringUtils.isNotEmpty(table.getPhoneRegexp())) {
            match = Pattern.matches(table.getPhoneRegexp(), customInfo.getPhone());
        }
        if (match && StringUtils.isNotEmpty(table.getCountryCodes())) {
            match = matchListField(customInfo.getCountry().trim(), table.getCountryCodes());
        }
        if (match && StringUtils.isNotEmpty(table.getStateEnNames())) {
            match = matchListField(customInfo.getState().trim(), table.getStateEnNames());
        }
        if (match && StringUtils.isNotEmpty(table.getCityEnNames())) {
            match = matchListField(customInfo.getCity().trim(), table.getCityEnNames());
        }
        return match;
    }

    private boolean matchListField(String value, String listStr) {
        if (value == null || listStr == null) {
            return false;
        }
        List<String> items;
        if (listStr.contains(",")) {
            items = Arrays.stream(listStr.split(","))
                    .map(String::trim)
                    .filter(StringUtils::isNotEmpty).collect(Collectors.toList());
        } else {
            items = Collections.singletonList(listStr.trim());
        }

        return items.contains(value);
    }
}
