package com.izouma.nineth.service;

import cn.hutool.core.util.ZipUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSONObject;
import com.izouma.nineth.annotations.RedisLock;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.SandPaySettle;
import com.izouma.nineth.dto.UserBankCard;
import com.izouma.nineth.dto.UserWithdraw;
import com.izouma.nineth.enums.BalanceType;
import com.izouma.nineth.enums.CollectionSource;
import com.izouma.nineth.enums.OrderStatus;
import com.izouma.nineth.enums.PayMethod;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.*;
import com.izouma.nineth.service.storage.StorageService;
import com.izouma.nineth.utils.DateTimeUtils;
import com.izouma.nineth.utils.SnowflakeIdWorker;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.util.TempFile;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.transaction.Transactional;
import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
@Slf4j
@AllArgsConstructor
public class UserBalanceService {
    private final UserBalanceRepo               userBalanceRepo;
    private final BalanceRecordRepo             balanceRecordRepo;
    private final OrderRepo                     orderRepo;
    private final AssetRepo                     assetRepo;
    private final UserBankCardRepo              userBankCardRepo;
    private final SettleRecordRepo              settleRecordRepo;
    private final StorageService                storageService;
    private final ExportWithdrawRepo            exportWithdrawRepo;
    private final AutoWithdrawRecordRepo        autoWithdrawRecordRepo;
    private final SnowflakeIdWorker             snowflakeIdWorker;
    private final SandPayService                sandPayService;
    private final RedisTemplate<String, Object> redisTemplate;
    private final RechargeOrderRepo             rechargeOrderRepo;
    private final SysConfigService              sysConfigService;
    private final Environment                   env;

    public void settle(LocalDate start, LocalDate end) {
        for (long i = 0; i <= ChronoUnit.DAYS.between(start, end); i++) {
            LocalDate date = start.plusDays(i);
            if (settleRecordRepo.findByDate(date).isPresent()) {
                throw new BusinessException(DateTimeUtils.format(date, "yyyy-MM-dd") + "已经结算过");
            }
        }
        for (long i = 0; i <= ChronoUnit.DAYS.between(start, end); i++) {
            LocalDate date = start.plusDays(i);
            settle(date);
        }
    }

    public void settle(LocalDate date) {
        if (settleRecordRepo.findByDate(date).isPresent()) {
            throw new BusinessException(DateTimeUtils.format(date, "yyyy-MM-dd") + "已经结算过");
        }
        List<Order> orders = orderRepo.findByCreatedAtBetweenAndSourceAndStatusIn(date.atStartOfDay(),
                date.atTime(23, 59, 59, 9999), CollectionSource.TRANSFER,
                Arrays.asList(OrderStatus.PROCESSING, OrderStatus.FINISH));
        List<Asset> assets = assetRepo.findAllById(orders.stream().map(Order::getAssetId).collect(Collectors.toSet()));

        BigDecimal totalAmount = BigDecimal.ZERO;
        BigDecimal royaltiesAmount = BigDecimal.ZERO;
        BigDecimal serviceChargeAmount = BigDecimal.ZERO;

        List<UserBalance> balanceList = new ArrayList<>();
        List<BalanceRecord> recordList = new ArrayList<>();
        int c = 0;
        for (Order order : orders) {
            log.info("结算订单 {}/{}, orderId={}", ++c, orders.size(), order.getId());
            BalanceRecord record = balanceRecordRepo.findByOrderIdAndType(order.getId(), BalanceType.SELL);
            if (record != null) {
                continue;
            }
            Asset asset = assets.stream().filter(i -> i.getId().equals(order.getAssetId()))
                    .findFirst()
                    .orElseThrow(new BusinessException("藏品不存在"));

            UserBalance userBalance = balanceList.stream().filter(b -> b.getUserId().equals(asset.getUserId()))
                    .findFirst().orElse(null);
            if (userBalance == null) {
                userBalance = userBalanceRepo.findById(asset.getUserId())
                        .orElse(new UserBalance(asset.getUserId(), BigDecimal.ZERO, BigDecimal.ZERO,
                                false, null, null));
                balanceList.add(userBalance);
            }

            BigDecimal amount = order.getTotalPrice()
                    .subtract(order.getGasPrice())
                    .multiply(BigDecimal.valueOf(100 - order.getRoyalties() - order.getServiceCharge()))
                    .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);

            totalAmount = totalAmount.add(order.getTotalPrice());
            royaltiesAmount = royaltiesAmount.add(order.getTotalPrice()
                    .subtract(order.getGasPrice())
                    .multiply(BigDecimal.valueOf(order.getRoyalties()))
                    .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP));
            serviceChargeAmount = serviceChargeAmount.add(order.getTotalPrice()
                    .subtract(order.getGasPrice())
                    .multiply(BigDecimal.valueOf(order.getServiceCharge()))
                    .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP));

            userBalance.setLastBalance(userBalance.getBalance());
            userBalance.setBalance(userBalance.getBalance().add(amount));

            recordList.add(BalanceRecord.builder()
                    .time(LocalDateTime.now())
                    .userId(asset.getUserId())
                    .orderId(order.getId())
                    .amount(amount)
                    .balance(userBalance.getBalance())
                    .lastBalance(userBalance.getLastBalance())
                    .type(BalanceType.SELL)
                    .build());
        }
        userBalanceRepo.saveAll(balanceList);
        balanceRecordRepo.saveAll(recordList);
        settleRecordRepo.save(new SettleRecord(date, orders.size(), totalAmount, royaltiesAmount, serviceChargeAmount));
    }

    @Async
    @Transactional
    public ExportWithdraw exportWithdrawAsync(String remark) throws IOException, InvalidFormatException {
        return exportWithdraw(remark);
    }

    @Transactional
    public ExportWithdraw exportWithdraw(String remark) throws IOException, InvalidFormatException {
        List<UserBalance> balanceList = userBalanceRepo.findByBalanceGreaterThan(BigDecimal.ZERO);
        List<UserBankCard> userBankCardList = balanceList.isEmpty() ? new ArrayList<>() :
                userBankCardRepo.findByUserIdIn(balanceList.stream().map(UserBalance::getUserId)
                        .collect(Collectors.toSet()));
        List<UserWithdraw> withdrawList = new ArrayList<>();
        Iterator<UserBalance> it = balanceList.iterator();
        UserBalance ub;
        while (it.hasNext()) {
            ub = it.next();
            Long userId = ub.getUserId();
            log.info("查询提现银行卡userId={}", ub.getUserId());
            UserBankCard ubc = userBankCardList.stream().filter(u -> u.getUserId().equals(userId))
                    .findFirst().orElse(null);
            if (ubc != null) {
                withdrawList.add(new UserWithdraw(userId, ubc.getRealName(), ubc.getBankNo(), ub.getBalance()));
            } else {
                it.remove();
            }
        }

        File tmpDir = TempFile.createTempDirectory("export_" + RandomStringUtils.randomAlphabetic(8));
        File file1 = new File(tmpDir, DateTimeUtils.format(LocalDate.now(), "yyyyMMdd") + "结算.xlsx");
        File file2 = new File(tmpDir, DateTimeUtils.format(LocalDate.now(), "yyyyMMdd") + "结算导入.xls");
        EasyExcel.write(file1, UserWithdraw.class)
                .sheet("sheet").doWrite(withdrawList);

        InputStream inputStream = getClass().getResourceAsStream("/批量付款到对私银行账户模板.xls");
        Workbook workbook = WorkbookFactory.create(inputStream);

        Sheet sheet = workbook.getSheetAt(0);

        for (int i = 0; i < withdrawList.size(); i++) {
            Row row = Optional.ofNullable(sheet.getRow(i + 1)).orElse(sheet.createRow(i + 1));

            Optional.ofNullable(row.getCell(1))
                    .orElse(row.createCell(1))
                    .setCellValue(withdrawList.get(i).getName());
            Optional.ofNullable(row.getCell(2))
                    .orElse(row.createCell(2))
                    .setCellValue(withdrawList.get(i).getBankNo());
            Optional.ofNullable(row.getCell(3))
                    .orElse(row.createCell(3))
                    .setCellValue(withdrawList.get(i).getAmount().doubleValue());
            Optional.ofNullable(row.getCell(4))
                    .orElse(row.createCell(4))
                    .setCellValue(withdrawList.get(i).getUserId());
        }

        inputStream.close();

        FileOutputStream os = new FileOutputStream(file2);
        workbook.write(os);

        workbook.close();

        File zipFile = ZipUtil.zip(tmpDir);
        String url = storageService.uploadFromInputStream(new FileInputStream(zipFile),
                "upload/" + DateTimeUtils.format(LocalDateTime.now(), "yyyyMMddHHmm") + "_" + RandomStringUtils.randomNumeric(8) + ".zip");

        ExportWithdraw exportWithdraw = exportWithdrawRepo.save(new ExportWithdraw(url, remark, withdrawList.size(),
                withdrawList.stream().map(UserWithdraw::getAmount).reduce(BigDecimal::add)
                        .orElse(BigDecimal.ZERO), "处理中"));

        balanceList.parallelStream().forEach(userBalance -> {
            log.info("提现userId={}", userBalance.getUserId());
            BigDecimal amount = userBalance.getBalance();
            userBalance.setLastBalance(userBalance.getBalance());
            userBalance.setBalance(BigDecimal.ZERO);
            userBalanceRepo.saveAndFlush(userBalance);

            balanceRecordRepo.save(BalanceRecord.builder()
                    .time(LocalDateTime.now())
                    .userId(userBalance.getUserId())
                    .amount(amount.negate())
                    .balance(BigDecimal.ZERO)
                    .lastBalance(userBalance.getLastBalance())
                    .type(BalanceType.WITHDRAW)
                    .build());
        });

        tmpDir.delete();
        zipFile.delete();

        return exportWithdraw;
    }

    @Transactional
    public void importFail(MultipartFile withdrawFile, MultipartFile settleFile) throws IOException {
        List<SandPaySettle> failSettleList = EasyExcel.read(settleFile.getInputStream())
                .head(SandPaySettle.class).sheet()
                .doReadSync();
        failSettleList = failSettleList.stream()
                .filter(s -> "失败".equals(s.getStatus().trim()))
                .collect(Collectors.toList());
        List<UserWithdraw> withdrawList = EasyExcel.read(withdrawFile.getInputStream())
                .head(UserWithdraw.class).sheet()
                .doReadSync();
        List<UserWithdraw> failWithdraw = new ArrayList<>();
        for (SandPaySettle sandPaySettle : failSettleList) {
            List<UserWithdraw> list;
            if (StringUtils.isNotBlank(sandPaySettle.getRemark()) && Pattern.matches("^\\d+$", sandPaySettle.getRemark()
                    .trim())) {
                Long userId = Long.parseLong(sandPaySettle.getRemark().trim());
                list = withdrawList.stream().filter(i -> i.getUserId().equals(userId))
                        .collect(Collectors.toList());
            } else {
                list = withdrawList.stream().filter(i -> i.getBankNo().equals(sandPaySettle.getBankNo())
                                && i.getName().equals(sandPaySettle.getName())
                                && i.getAmount().compareTo(sandPaySettle.getAmount()) == 0)
                        .collect(Collectors.toList());
            }
            if (list.size() != 1) {
                throw new BusinessException("不唯一：" + sandPaySettle.getName() + "," + sandPaySettle.getBankNo());
            } else {
                failWithdraw.add(list.get(0));
            }
        }
        failWithdraw.parallelStream().forEach(withdraw -> {
            UserBalance userBalance = userBalanceRepo.findById(withdraw.getUserId())
                    .orElse(new UserBalance(withdraw.getUserId(), BigDecimal.ZERO, BigDecimal.ZERO,
                            false, null, null));
            userBalance.setLastBalance(userBalance.getBalance());
            userBalance.setBalance(userBalance.getBalance().add(withdraw.getAmount()));
            userBalanceRepo.saveAndFlush(userBalance);

            balanceRecordRepo.save(BalanceRecord.builder()
                    .time(LocalDateTime.now())
                    .userId(userBalance.getUserId())
                    .amount(withdraw.getAmount())
                    .balance(userBalance.getBalance())
                    .lastBalance(userBalance.getLastBalance())
                    .type(BalanceType.RETURN)
                    .build());
        });
    }

    @Async
    public void autoWithdraw(LocalDate date) throws ExecutionException, InterruptedException {
        ForkJoinPool customThreadPool = new ForkJoinPool(5);
        customThreadPool.submit(() -> {
            autoWithdrawRecordRepo.findByDate(date).ifPresent(a -> {
                throw new BusinessException("今日已经提现过");
            });

            List<UserBalance> list = userBalanceRepo.findByLockedFalseAndBalanceGreaterThanOrderByUserId(BigDecimal.ZERO);

            AutoWithdrawRecord record = AutoWithdrawRecord.builder()
                    .date(LocalDate.now())
                    .status("pending")
                    .progress(0)
                    .total(list.size())
                    .build();
            autoWithdrawRecordRepo.saveAndFlush(record);

            list.parallelStream().forEach(userBalance -> {
                UserBankCard userBankCard = userBankCardRepo.findByUserId(userBalance.getUserId())
                        .stream().findFirst().orElse(null);
                if (userBankCard == null) {
                    log.info("自动提现userId={}, amount={}, 未绑卡", userBalance.getBalance(), userBalance.getUserId());
                    record.setProgress(record.getProgress() + 1);
                    record.setCurrentUserId(userBalance.getUserId());
                    autoWithdrawRecordRepo.saveAndFlush(record);
                } else {
                    log.info("自动提现userId={}, amount={}, name={}, bank={}",
                            userBalance.getUserId(), userBalance.getBalance(),
                            userBankCard.getRealName(), userBankCard.getBankNo());

                    String withdrawId = snowflakeIdWorker.nextId() + "";

                    BigDecimal amount = userBalance.getBalance();
                    userBalance.setLastBalance(userBalance.getBalance());
                    userBalance.setBalance(BigDecimal.ZERO);
                    userBalanceRepo.saveAndFlush(userBalance);

                    balanceRecordRepo.save(BalanceRecord.builder()
                            .time(LocalDateTime.now())
                            .userId(userBalance.getUserId())
                            .amount(amount.negate())
                            .balance(BigDecimal.ZERO)
                            .lastBalance(userBalance.getLastBalance())
                            .type(BalanceType.WITHDRAW)
                            .withdrawId(withdrawId)
                            .build());

                    boolean success = false;
                    String msg = null;
                    try {
                        JSONObject res = sandPayService.transfer(withdrawId, userBankCard.getRealName(), userBankCard.getBankNo(), amount, userBankCard.getPhone());
                        if ("0000".equals(res.getString("respCode"))) {
                            success = true;
                        } else {
                            msg = res.getString("respDesc");
                        }
                    } catch (Exception e) {
                        msg = e.getMessage();
                    }

                    if (!success) {
                        userBalance.setLastBalance(userBalance.getBalance());
                        userBalance.setBalance(userBalance.getBalance().add(amount));
                        userBalanceRepo.saveAndFlush(userBalance);

                        balanceRecordRepo.save(BalanceRecord.builder()
                                .time(LocalDateTime.now())
                                .userId(userBalance.getUserId())
                                .amount(amount)
                                .balance(userBalance.getBalance())
                                .lastBalance(userBalance.getLastBalance())
                                .type(BalanceType.RETURN)
                                .withdrawId(withdrawId)
                                .remark(msg)
                                .build());
                    }

                    record.setProgress(record.getProgress() + 1);
                    record.setCurrentUserId(userBalance.getUserId());
                    autoWithdrawRecordRepo.saveAndFlush(record);

                    if (!success) {
                        userBalance.setLocked(true);
                        userBalance.setLockReason(msg);
                        userBalance.setLockTime(LocalDateTime.now());
                        userBalanceRepo.saveAndFlush(userBalance);
                    }
                }
            });

            record.setStatus("finish");
            autoWithdrawRecordRepo.saveAndFlush(record);
        }).get();
    }

    public void revert() throws ExecutionException, InterruptedException {
        ForkJoinPool customThreadPool = new ForkJoinPool(200);
        customThreadPool.submit(() -> {
            LocalDate now = LocalDate.now();
            userBalanceRepo.findAll().parallelStream().forEach(userBalance -> {
                List<BalanceRecord> balanceRecords = balanceRecordRepo.findByUserIdOrderByCreatedAt(userBalance.getUserId());
                List<BalanceRecord> todayRecords = balanceRecords.stream()
                        .filter(b -> b.getCreatedAt().toLocalDate().equals(now)).collect(Collectors.toList());
                List<BalanceRecord> oldRecords = balanceRecords.stream()
                        .filter(b -> !b.getCreatedAt().toLocalDate().equals(now))
                        .sorted(Comparator.comparing(BaseEntity::getCreatedAt))
                        .collect(Collectors.toList());
                if (oldRecords.size() == 0) {
                    userBalanceRepo.delete(userBalance);
                } else {
                    BalanceRecord record = oldRecords.get(oldRecords.size() - 1);
                    userBalance.setBalance(record.getBalance());
                    userBalance.setLastBalance(record.getLastBalance());
                    userBalanceRepo.save(userBalance);
                }
                balanceRecordRepo.deleteAll(todayRecords);
            });
        }).get();

    }

    @RedisLock("#userId")
    public BalanceRecord balancePay(Long userId, BigDecimal amount, Long orderId, String remark) {
        UserBalance userBalance = userBalanceRepo.findById(userId).orElseThrow(new BusinessException("余额不足"));
        if (userBalance.getBalance().compareTo(amount) < 0) {
            throw new BusinessException("余额不足");
        }
        userBalance.setLastBalance(userBalance.getBalance());
        userBalance.setBalance(userBalance.getBalance().subtract(amount));
        userBalanceRepo.save(userBalance);

        return balanceRecordRepo.save(BalanceRecord.builder()
                .userId(userId)
                .balance(userBalance.getBalance())
                .lastBalance(userBalance.getLastBalance())
                .type(BalanceType.PAY)
                .amount(amount.negate())
                .time(LocalDateTime.now())
                .orderId(orderId)
                .remark(remark)
                .build());
    }

    @RedisLock("#userId")
    public void recharge(Long orderId, PayMethod payMethod, String transactionId) {
        log.info("recharge  orderId={}, transactionId={}, payMethod={}",
                orderId, transactionId, payMethod);
        RechargeOrder rechargeOrder = rechargeOrderRepo.findById(orderId).orElseThrow(new BusinessException("充值订单不存在"));
        Long userId = rechargeOrder.getUserId();
        if (OrderStatus.NOT_PAID == rechargeOrder.getStatus()) {
            BigDecimal amount = rechargeOrder.getAmount();
            UserBalance userBalance = userBalanceRepo.findById(userId).orElse(UserBalance.builder()
                    .userId(userId)
                    .balance(BigDecimal.ZERO)
                    .lastBalance(BigDecimal.ZERO)
                    .build());
            userBalance.setLastBalance(userBalance.getBalance());
            userBalance.setBalance(userBalance.getBalance().add(amount));
            userBalanceRepo.save(userBalance);

            balanceRecordRepo.save(BalanceRecord.builder()
                    .userId(userId)
                    .balance(userBalance.getBalance())
                    .lastBalance(userBalance.getLastBalance())
                    .type(BalanceType.RECHARGE)
                    .amount(amount)
                    .time(LocalDateTime.now())
                    .orderId(orderId)
                    .payMethod(payMethod)
                    .build());
        } else if (OrderStatus.FINISH == rechargeOrder.getStatus()) {
            log.info("recharge orderId={} has been finished", orderId);
        } else {
            throw new BusinessException("充值订单状态异常");
        }

    }

    public BalanceRecord preWithdraw(Long userId, BigDecimal amount) {
        UserBalance userBalance = userBalanceRepo.findById(userId).orElseThrow(new BusinessException("余额不足"));
        if (amount.compareTo(userBalance.getBalance()) > 0) {
            throw new BusinessException("余额不足");
        }
        BigDecimal minWithdrawAmount = sysConfigService.getBigDecimal("min_withdraw_amount");
        if (amount.compareTo(minWithdrawAmount) < 0) {
            throw new BusinessException("最小提现金额为" + minWithdrawAmount);
        }
        UserBankCard userBankCard = userBankCardRepo.findByUserId(userBalance.getUserId())
                .stream().findFirst().orElseThrow(new BusinessException("请先绑定银行卡"));

        userBalance.setLastBalance(userBalance.getBalance());
        userBalance.setBalance(userBalance.getBalance().subtract(amount));
        userBalanceRepo.saveAndFlush(userBalance);

        return balanceRecordRepo.save(BalanceRecord.builder()
                .time(LocalDateTime.now())
                .userId(userBalance.getUserId())
                .amount(amount.negate())
                .balance(userBalance.getBalance())
                .lastBalance(userBalance.getLastBalance())
                .type(BalanceType.WITHDRAW)
                .build());
    }

    @RedisLock(value = "'modifyBalance::'+#userId", behavior = RedisLock.Behavior.WAIT)
    public void modifyBalance(Long userId, BigDecimal amount, BalanceType type,
                              String reason, boolean lock, String withdrawId) {
        UserBalance userBalance = userBalanceRepo.findById(userId).orElse(new UserBalance(userId));
        userBalance.setLastBalance(userBalance.getBalance());
        userBalance.setBalance(userBalance.getBalance().add(amount));
        if (lock) {
            userBalance.setLockTime(LocalDateTime.now());
            userBalance.setLocked(true);
            userBalance.setLockReason(reason);
        }
        userBalanceRepo.saveAndFlush(userBalance);

        balanceRecordRepo.save(BalanceRecord.builder()
                .time(LocalDateTime.now())
                .userId(userId)
                .amount(amount)
                .balance(userBalance.getBalance())
                .lastBalance(userBalance.getLastBalance())
                .type(type)
                .remark(reason)
                .withdrawId(withdrawId)
                .build());
    }

    public void realtimeSettleOrder(Order order) {
        if (!sysConfigService.getBoolean("realtime_settle_order")) {
            return;
        }
        BalanceRecord record = balanceRecordRepo.findByOrderIdAndType(order.getId(), BalanceType.SELL);
        if (record != null) {
            log.info("此订单已结算 orderId={}", order.getId());
            return;
        }
        log.info("结算订单 orderId={}", order.getId());
        Asset asset = assetRepo.findById(order.getAssetId()).orElse(null);

        UserBalance userBalance = userBalanceRepo.findById(asset.getUserId())
                .orElse(new UserBalance(asset.getUserId()));

        BigDecimal amount = order.getTotalPrice()
                .subtract(order.getGasPrice())
                .multiply(BigDecimal.valueOf(100 - order.getRoyalties() - order.getServiceCharge()))
                .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);

        userBalance.setLastBalance(userBalance.getBalance());
        userBalance.setBalance(userBalance.getBalance().add(amount));
        userBalanceRepo.save(userBalance);

        balanceRecordRepo.save(BalanceRecord.builder()
                .time(LocalDateTime.now())
                .userId(asset.getUserId())
                .orderId(order.getId())
                .amount(amount)
                .balance(userBalance.getBalance())
                .lastBalance(userBalance.getLastBalance())
                .type(BalanceType.SELL)
                .build());
    }

    public void addBalance(Long userId, BigDecimal amount, Long orderId, BalanceType type) {
        //用户冲余额
        UserBalance userBalance = userBalanceRepo.findByUserId(userId)
                .orElse(UserBalance.builder()
                        .balance(BigDecimal.ZERO)
                        .lastBalance(BigDecimal.ZERO)
                        .userId(userId)
                        .build());

        userBalance.setLastBalance(userBalance.getBalance());
        userBalance.setBalance(userBalance.getBalance().add(amount));
        userBalanceRepo.save(userBalance);
        log.info("拍卖冲用户余额{},¥{}", userId, amount);

        balanceRecordRepo.save(BalanceRecord.builder()
                .time(LocalDateTime.now())
                .userId(userId)
                .orderId(orderId)
                .amount(amount)
                .balance(userBalance.getBalance())
                .lastBalance(userBalance.getLastBalance())
                .type(type)
                .build());
    }

    @Async
    public void cancelRefundGas() throws ExecutionException, InterruptedException {
        new ForkJoinPool(500).submit(() -> {
            List<BalanceRecord> recordList = balanceRecordRepo.findByType(BalanceType.REFUND);
            recordList.parallelStream().forEach(r -> {
                log.info("cancelRefundGas {}", r.getUserId());
                userBalanceRepo.modifyBalance(r.getUserId(), new BigDecimal("-1"));
                balanceRecordRepo.delete(r);
            });
        }).get();
    }
}