package com.crm.ucard.service.impl;

import com.alibaba.fastjson.JSON;
import com.crm.rely.backend.core.constant.Constants;
import com.crm.rely.backend.core.constant.PayConstant;
import com.crm.rely.backend.core.dto.base.BaseReportResultDto;
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.ucard.*;
import com.crm.rely.backend.core.dto.wasabi.*;
import com.crm.rely.backend.core.entity.base.BaseEntity;
import com.crm.rely.backend.core.entity.custom.info.InfoEntity;
import com.crm.rely.backend.core.entity.ucard.*;
import com.crm.rely.backend.core.entity.wasabi.*;
import com.crm.rely.backend.core.enums.*;
import com.crm.rely.backend.core.pojo.table.ucard.*;
import com.crm.rely.backend.exception.ServiceException;
import com.crm.rely.backend.service.MqSendService;
import com.crm.rely.backend.service.RedisService;
import com.crm.rely.backend.util.BeanUtilPlus;
import com.crm.rely.backend.util.DateUtil;
import com.crm.rely.backend.util.GetRandom;
import com.crm.rely.backend.util.ListSplitUtil;
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.service.GlobalService;
import com.crm.ucard.service.UcardEmailService;
import com.crm.ucard.service.WasabiDocumentApiService;
import com.crm.ucard.util.ObjectIdUtil;
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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

@Slf4j
@Service
public class GlobalServiceImpl implements GlobalService {

    @Autowired
    private UcardCardRepository cardRepository;
    @Autowired
    private CardRechargeOrderRepository rechargeOrderRepository;
    @Autowired
    private CardMerchantUserRepository merchantUserRepository;
    @Autowired
    private CardOperateRepository cardOperateRepository;
    @Autowired
    private UcardWalletRepository ucardWalletRepository;
    @Autowired
    private UcardWalletRecordRepository ucardWalletRecordRepository;
    @Autowired
    private CardGlobalCurrenciesRepository globalCurrenciesRepository;
    @Autowired
    private CardGlobalOrderDataAvailableRepository globalOrderDataAvailableRepository;
    @Autowired
    private CardGlobalOrderDataRepository globalOrderDataRepository;
    @Autowired
    private CardGlobalOrderRepository globalOrderRepository;
    @Autowired
    private CardGlobalStatesMapper globalStatesMapper;
    @Autowired
    private CardGlobalCurrenciesMapper globalCurrenciesMapper;
    @Autowired
    private CardGlobalAvailableBankCitiesMapper globalAvailableBankCitiesMapper;
    @Autowired
    private MqSendService mqSendService;
    @Autowired
    private CardGlobalCurrenciesFieldMapper globalCurrenciesFieldMapper;
    @Autowired
    private CardGlobalOrderMapper globalOrderMapper;
    @Autowired
    private CardMerchantUserMapper merchantUserMapper;
    @Autowired
    private CardGlobalFeeRateConfigRepository globalFeeRateConfigRepository;
    @Autowired
    private UcardGlobalReceiverInfoRepository globalReceiverInfoRepository;
    @Autowired
    private CardWithdrawRepository withdrawRepository;
    @Autowired
    private WasabiDocumentApiService wasabiDocumentApiService;
    @Autowired
    private UcardEmailService ucardEmailService;
    @Autowired
    private RedisService redisService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto saveCurrencies() throws Exception {
        return saveAvailableCurrencies();
    }

    /**
     * 同步州表数据和银行城市信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveGlobalStates(List<CardGlobalCurrenciesTable> currenciesTables) throws Exception {
        // 删除州表数据
        globalStatesMapper.deleteAll();
        Map<String, String> countryCodeMap = null;

        // 查询省/州数据
        List<WasabiStatesDto> statesDtos = wasabiDocumentApiService.getStates().getData();
        if (CollectionUtils.isEmpty(statesDtos)) {
            log.error("同步州表数据和银行城市信息: 州数据为空");
            return;
        }

        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<CardGlobalAvailableBankCitiesTable> bankCitiesTables = new ArrayList<>();
        for (CardGlobalCurrenciesTable currenciesTable : currenciesTables) {
            // 处理币种支持的城市列表
            if (StringUtils.isNotEmpty(currenciesTable.getPayoutCurrency())) {
                WasabiAvailableBankCitiesEntity bankCitiesEntity = new WasabiAvailableBankCitiesEntity();
                bankCitiesEntity.setPayoutCurrency(currenciesTable.getPayoutCurrency());
                bankCitiesEntity.setParentCodeId(countryCodeMap.get(currenciesTable.getCountry()));
                List<WasabiMethodDto> data = wasabiDocumentApiService.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());
                        bankCitiesTable.setAddTime(new Date());
                        bankCitiesTables.add(bankCitiesTable);
                    }
                }
            }
        }
        // 删除银行城市名单
        globalAvailableBankCitiesMapper.deleteAll();
        if (CollectionUtils.isNotEmpty(bankCitiesTables)) {
            List<List<CardGlobalAvailableBankCitiesTable>> bankCitiesLists = ListSplitUtil.split(bankCitiesTables);
            for (List<CardGlobalAvailableBankCitiesTable> bankCitiesList : bankCitiesLists) {
                globalAvailableBankCitiesMapper.batchSave(bankCitiesList);
            }
        }
    }

    /**
     * 速汇-更新支持的币种、银行城市名单、州列表、币种发送交易需要的字段
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto saveAvailableCurrencies() throws Exception {
        Date now = new Date();
        // 查询支持的币种
        List<WasabiAvailableCurrenciesDto> currenciesDtos = wasabiDocumentApiService.getAvailableCurrencies().getData();
        if (CollectionUtils.isEmpty(currenciesDtos)) {
            throw new ServiceException("同步币种失败,币种信息为空");
        }
        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(now);
                    return currenciesTable;
                }).distinct().collect(Collectors.toList());// 添加去重操作
        if (CollectionUtils.isEmpty(currenciesTables)) {
            throw new ServiceException("同步币种失败,币种数据为空");
        }
        List<WasabiFieldParamsDto> fieldParamList = new ArrayList<>();
        List<CardGlobalCurrenciesFieldTable> currenciesFieldTables = new ArrayList<>();
        // 删除支持的币种数据
        globalCurrenciesMapper.deleteAll();
        // 遍历币种查询支持的字段
        for (CardGlobalCurrenciesTable currenciesTable : currenciesTables) {
            WasabiAvailableFieldParamsEntity fieldParamsEntity = new WasabiAvailableFieldParamsEntity();
            fieldParamsEntity.setPayoutCurrency(currenciesTable.getPayoutCurrency());
            fieldParamsEntity.setTransferType(currenciesTable.getTransferTypeId());
            fieldParamsEntity.setPayoutMethod(currenciesTable.getPayoutMethodId());
            // 查询币种发送交易需要的字段
            WasabiAvailableFieldParamsDto availableFieldParamsDto =
                    wasabiDocumentApiService.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());
                fieldParamList.addAll(fieldParams);
                // 遍历字段数据 获取字段可选值列表
                for (WasabiFieldParamsDto fieldParamsDto : fieldParams) {
                    CardGlobalCurrenciesFieldTable currenciesFieldTable = new CardGlobalCurrenciesFieldTable();
                    List<WasabiMethodDto> selectValues = fieldParamsDto.getSelectValues();
                    BeanUtils.copyProperties(fieldParamsDto, currenciesFieldTable);
                    currenciesFieldTable.setPayoutCurrencyId(currenciesTableId);
                    currenciesFieldTable.setAddTime(now);
                    if (CollectionUtils.isNotEmpty(selectValues)) {
                        currenciesFieldTable.setSelectValues(JSON.toJSONString(selectValues));
                    }
                    currenciesFieldTables.add(currenciesFieldTable);
                }
            }
        }
        // 删除币种发送交易需要的字段
        globalCurrenciesFieldMapper.deleteAll();
        if (CollectionUtils.isNotEmpty(currenciesFieldTables)) {
            List<List<CardGlobalCurrenciesFieldTable>> lists =
                    ListSplitUtil.split(currenciesFieldTables);
            for (List<CardGlobalCurrenciesFieldTable> fieldAvailableList : lists) {
                globalCurrenciesFieldMapper.batchSave(fieldAvailableList);
            }
        }
        if (CollectionUtils.isNotEmpty(fieldParamList)) {
            saveCache(fieldParamList);
        }
        mqSendService.send(Constants.SAVE_GLOBAL_BANK_CITIES, currenciesTables);
        return BaseResultDto.success();
    }

    /**
     * 更新全部币种字段缓存
     */
    public void saveCache(List<WasabiFieldParamsDto> fieldParamList) {
        try {
            // 缓存币种字段列表
            List<WasabiFieldParamsDto> distinctList = fieldParamList.stream()
                    .filter(dto -> dto.getFieldName() != null)  // 过滤掉fieldName为null的元素
                    .map(dto -> {
                        // 创建新对象并置空selectValues字段
                        WasabiFieldParamsDto newDto = new WasabiFieldParamsDto();
                        BeanUtils.copyProperties(dto, newDto);
                        newDto.setSelectValues(null); // 置空selectValues字段
                        return newDto;
                    })
                    .collect(Collectors.toMap(
                            WasabiFieldParamsDto::getFieldName,  // 以fieldName作为key
                            dto -> dto,                          // value为dto本身
                            (existing, replacement) -> existing  // 如果key重复，保留第一个
                    ))
                    .values()
                    .stream()
                    .collect(Collectors.toList());
            redisService.saveList(Constants.WASABI_FIELDPARAMS, distinctList);
        } catch (Exception e) {
            log.error("缓存币种字段列表失败", e);
        }
    }

    /**
     * 获取所有币种字段列表
     */
    @Override
    public BaseResultDto getGlobalFieldParams() throws ServiceException {
        List<WasabiFieldParamsDto> fieldParamsDtos = redisService.getList(Constants.WASABI_FIELDPARAMS, WasabiFieldParamsDto.class);
        if (CollectionUtils.isEmpty(fieldParamsDtos)) {
            fieldParamsDtos = globalCurrenciesFieldMapper.getDistinctAll();
            if (CollectionUtils.isNotEmpty(fieldParamsDtos)) {
                saveCache(fieldParamsDtos);
            }
        }
        return BaseResultDto.success(fieldParamsDtos);
    }

    /**
     * 币种分页列表
     */
    @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 Exception {
        // 1. 获取三方接口返回的汇率数据
        BaseResultDto<List<WasabiExchangeRateDto>> wasabiResult = wasabiDocumentApiService.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) {
                if (StringUtils.isNotEmpty(dto.getSelectValues())) {
                    List<WasabiFieldAvailableDto> availableDtos = JSON.parseArray(dto.getSelectValues(), WasabiFieldAvailableDto.class);
                    dto.setAvailableDtos(availableDtos);
                }
            }
        }
        return BaseResultDto.success(dtos);
    }

    /**
     * 查询汇率计算手续费
     */
    @Override
    public GlobalQueryExchangeRateDto queryExchangeRate(GlobalQueryExchangeRateEntity entity) throws Exception {
        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));
            configTable.setFeeRateConfig(currenciesTable.getFeeRate());
            configTable.setFixedFeeConfig(currenciesTable.getFixedFee());
        }
        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));
        }
        if (configTable.getFeeRateConfig() == null && configTable.getFixedFeeConfig() == null) {
            CardFeeRateConfigTable table = globalFeeRateConfigRepository.findByType("1");
            if (table != null) {
                configTable.setFeeRateConfig(table.getFeeRateConfig());
                configTable.setFixedFeeConfig(table.getFixedFeeConfig());
            } else {
                configTable.setFeeRateConfig(currenciesTable.getFeeRate());
                configTable.setFixedFeeConfig(currenciesTable.getFixedFee());
            }
        }
        // 查询手续费率 固定手续费 计算手续费
        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
    @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());
        // 计算三方预估手续费
        BigDecimal fee = getFee(entity.getAmount(), entity.getPayoutCurrency(), entity.getTransferType(), entity.getPayoutMethod());
        String deductionMerchantOrderNo = null;
        String deductionAccountStatus = null;
        WasabiConfig config = wasabiDocumentApiService.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(CardWalletRecordTypeEnum.GLOBAL_DEDUCTION.getCode());
            walletRecordTable.setAddTime(new Date());
            ucardWalletRecordRepository.save(walletRecordTable);
            // 将收款人信息保存到数据库
            CardGlobalReceiverInfoTable receiverInfoTable =
                    globalReceiverInfoRepository.findByUniqueIdAndPayoutCurrencyAndTransferTypeAndPayoutMethodAndReceiverBankAccountNumber(
                            entity.getUniqueId(), entity.getPayoutCurrency(), entity.getTransferType(), entity.getPayoutMethod(),
                            entity.getReceiverBankAccountNumber());
            if (receiverInfoTable == null) {
                receiverInfoTable = new CardGlobalReceiverInfoTable();
                receiverInfoTable.setAddTime(new Date());
            } else {
                receiverInfoTable.setModifyTime(new Date());
            }
            BeanUtilPlus.copyPropertiesIgnoreNull(entity, receiverInfoTable);
            globalReceiverInfoRepository.save(receiverInfoTable);
            deductionAccountStatus = GlobalOrderDeductionStatusEnum.DEDUCTION_SUCCESS.getStatus();
        } else if ("1".equals(entity.getDeductionAccountType())) {
            UcardCardTable cardTable = cardRepository.getByCardNumber(entity.getCardNumber());
            WasabiBalanceInfoEntity infoEntity = new WasabiBalanceInfoEntity();
            infoEntity.setCardNo(cardTable.getCardNo());
            WasabiBalanceInfoDto infoDto = wasabiDocumentApiService.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 = wasabiDocumentApiService.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.setFee(fee);
        table.setAddTime(new Date());
        table.setNeedSupplement(false);
        globalOrderRepository.save(table);
        CardOperateTable operateTable = new CardOperateTable();
        operateTable.setUniqueId(userTable.getUniqueId());
        operateTable.setStatus(Constants.SUCCESS);
        operateTable.setOperateType(CardOperateTypeEnum.GLOBAL_CREATE_ORDER.getCode());
        operateTable.setOperateUser(entity.getCId());
        operateTable.setAddTime(new Date());
        cardOperateRepository.save(operateTable);
        // 给审核人员发邮件
        ucardEmailService.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 (!GlobalOrderDeductionStatusEnum.DEDUCTION_SUCCESS.getStatus().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(CardOperateTypeEnum.GLOBAL_ORDER_CANCEL.getCode());
        operateTable.setOperateUser(entity.getCId());
        operateTable.setAddTime(new Date());
        cardOperateRepository.save(operateTable);
        CardMerchantUserTable userTable = merchantUserRepository.findByUniqueId(table.getUniqueId());
        // 订单取消给客户发送邮件
        ucardEmailService.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 BaseReportResultDto<List<GlobalOrderDto>, GlobalOrderSumDto> 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 new BaseReportResultDto(Constants.SUCCESS_CODE, Constants.SEARCH_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()));
                });
            }
            GlobalOrderSumDto sumDto = globalOrderMapper.globalOrderSum(entity);
            GlobalOrderSumDto dto = GlobalOrderSumDto.getInstance();
            if (sumDto != null) {
                BeanUtils.copyProperties(sumDto, dto);
            }
            return new BaseReportResultDto(dtos, dto, pageDto);
        } 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 {
        if (entity.getIds() == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        Map<String, String> errorMap = new HashMap<>();
        for (Long id : entity.getIds()) {
            CardGlobalOrderTable table = globalOrderRepository.findFirstById(id);
            if (table == null) {
                return BaseResultDto.error(Constants.INFO_NOT_FOUND);
            }
            try {
                if (!GlobalOrderDeductionStatusEnum.DEDUCTION_SUCCESS.getStatus().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());
                table.setApproveTime(entity.getApproveTime());
                table.setApproveUser(entity.getApproveUser());
                if (entity.getStatus() == 2) {
                    AtomicBoolean needSupplement = new AtomicBoolean(false);
                    // 审核通过，调用三方创建订单
                    WasabiConfig config = wasabiDocumentApiService.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 = wasabiDocumentApiService.transactionTransfer(transactionTransfer, config);
                    List<WasabiRfiInfosDto> rfiInfos = transferDto.getRfiInfos();
                    if (CollectionUtils.isNotEmpty(rfiInfos)) {
                        rfiInfos.forEach(item -> {
                            WasabiRfiFieldInfoDto rfiFieldInfo = item.getRfiFieldInfo();
                            // status in (processing, partner_processing) 并且
                            // rfiInfos[0].status in (pending_check, rejected)时可提交合规检查资料
                            if ((transferDto.getStatus().equals(GlobalOrderStatusEnum.PROCESSING.getStatus()) || transferDto.getStatus().equals(GlobalOrderStatusEnum.PARTNER_PROCESSING.getStatus()))
                                    && (GlobalComplianceStatusEnum.PENDING_CHECK.getStatus().equals(item.getStatus()) || GlobalComplianceStatusEnum.REJECTED.getStatus().equals(item.getStatus()))) {
                                needSupplement.set(true);
                            }
                            CardGlobalOrderDataTable dataTable = new CardGlobalOrderDataTable();
                            BeanUtils.copyProperties(item, dataTable);
                            BeanUtils.copyProperties(rfiFieldInfo, dataTable);
                            dataTable.setMerchantOrderNo(transferDto.getMerchantOrderNo());
                            dataTable.setAddTime(new Date());
                            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());
                                    availableTable.setAddTime(new Date());
                                    globalOrderDataAvailableRepository.save(availableTable);
                                }
                            }
                        });
                    }
                    BeanUtils.copyProperties(transferDto, table);
                    table.setApproveStatus(2);
                    table.setNeedSupplement(needSupplement.get());
                    globalOrderRepository.save(table);
                    if (needSupplement.get()) {
                        // 待补充资料给客户发邮件
                        ucardEmailService.sendGlobalOrderSupplementaryDataEmail(userTable, table);
                    }
                    if (Constants.FAIL.equals(transferDto.getStatus())) {
                        // 退款
                        refundTransacOrder(table);
                        // 交易失败 给客户发邮件
                        ucardEmailService.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);
                    // 审核失败给用户发邮件
                    ucardEmailService.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(CardOperateTypeEnum.GLOBAL_ORDER_APPROVAL.getCode());
                operateTable.setOperateUser(entity.getOperateUser());
                operateTable.setAddTime(new Date());
                cardOperateRepository.save(operateTable);
            } catch (Exception e) {
                log.error("globalOrderApprove error", e);
                errorMap.put(table.getMerchantOrderNo(), e.getMessage());
            }
        }
        if (!errorMap.isEmpty()) {
            return BaseResultDto.result(Constants.FAILED_CODE, null, errorMap);
        }

        return BaseResultDto.success();
    }

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

            ResultData<WasabiDepositDto> resultData = wasabiDocumentApiService.cardDeposit(balanceEntity, config);

            WasabiDepositDto dto = resultData.getData();
            CardRechargeOrderTable rechargeTable = new CardRechargeOrderTable();
            BeanUtils.copyProperties(dto, rechargeTable);
            rechargeTable.setTransactionTime(DateUtil.formatTimestamp(dto.getTransactionTime()));
            rechargeTable.setAddTime(new Date());
            rechargeTable.setApproveStatus(CardApproveStatusEnum.APPROVED.getCode());
            rechargeTable.setCurrency("USD");
            rechargeTable.setReceivedCurrency("USD");
            rechargeTable.setRechargeType(CardRechargeTypeEnum.GLOBAL_REFUND.getCode());
            rechargeTable.setCardNumber(table.getCardNumber());
            rechargeTable.setUniqueId(table.getUniqueId());
            rechargeTable.setRechargeFee(BigDecimal.ZERO);
            rechargeOrderRepository.save(rechargeTable);
            String status;
            if (Constants.SUCCESS.equals(dto.getStatus())) {
                status = GlobalOrderDeductionStatusEnum.REFUND_SUCCESS.getStatus();
            } else if (Constants.FAIL.equals(dto.getStatus())) {
                status = GlobalOrderDeductionStatusEnum.REFUND_FAIL.getStatus();
            } else {
                status = GlobalOrderDeductionStatusEnum.REFUND_PROCESSING.getStatus();
            }
            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(CardWalletRecordTypeEnum.GLOBAL_REFUND.getCode());
            walletRecordTable.setAddTime(new Date());
            ucardWalletRecordRepository.save(walletRecordTable);
            table.setDeductionAccountStatus(GlobalOrderDeductionStatusEnum.REFUND_SUCCESS.getStatus());
            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 = wasabiDocumentApiService.uploadFile(item.getRfiValueUrl(), "globalTransfer");
                item.setRfiValue(rfiValue);
            }
        }
        wasabiDocumentApiService.transactionSubmitRfi(entity);
        table.setNeedSupplement(false);
        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(CardOperateTypeEnum.GLOBAL_SUBMIT_DATA.getCode());
        operateTable.setOperateUser(entity.getCId());
        operateTable.setAddTime(new Date());
        cardOperateRepository.save(operateTable);
        return BaseResultDto.success();
    }

    /**
     * 最近交易收款人列表
     */
    @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();
    }


    /**
     * 验证金额小数位数不能超过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 + "位");
                }
            }
        }
    }

    /**
     * 预估速汇交易三方手续费
     */
    public BigDecimal getFee(BigDecimal amount, String payoutCurrency, String transferTypeId, String payoutMethodId) {
        BigDecimal feeRate = BigDecimal.ZERO;
        BigDecimal fixedFee = BigDecimal.ZERO;
        CardGlobalCurrenciesTable table = globalCurrenciesRepository.findByPayoutCurrencyAndTransferTypeIdAndPayoutMethodId(payoutCurrency, transferTypeId, payoutMethodId);
        if (table.getFeeRate() != null && table.getFeeRate().compareTo(BigDecimal.ZERO) >= 0) {
            feeRate = table.getFeeRate();
        }
        if (table.getFixedFee() != null && table.getFixedFee().compareTo(BigDecimal.ZERO) >= 0) {
            fixedFee = table.getFixedFee();
        }
        BigDecimal feeRateAmount = amount.multiply(feeRate).divide(new BigDecimal("100"), 2,
                RoundingMode.FLOOR);
        BigDecimal fee = feeRateAmount.add(fixedFee);
        return fee;
    }


}
