package com.m2pool.lease.service.impl;

import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.m2pool.common.core.utils.DateUtils;
import com.m2pool.common.core.utils.StringUtils;
import com.m2pool.common.redis.service.RedisService;
import com.m2pool.common.security.utils.SecurityUtils;
import com.m2pool.lease.constant.CoinCharge;
import com.m2pool.lease.redis.RedisCoinInfoKey;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.dto.v2.*;
import com.m2pool.lease.entity.*;
import com.m2pool.lease.exception.OrderException;
import com.m2pool.lease.mapper.*;
import com.m2pool.lease.netty.handler.ChannelManager;
import com.m2pool.lease.netty.message.ClientMessage;
import com.m2pool.lease.service.LeaseOrderInfoService;
import com.m2pool.lease.service.LeaseOrderItemService;
import com.m2pool.lease.service.LeaseProductService;
import com.m2pool.lease.service.LeaseUserOwnedProductService;
import com.m2pool.lease.utils.UuidGeneratorUtil;
import com.m2pool.lease.utils.WalletRuleCheckUtils;
import com.m2pool.lease.vo.*;
import com.m2pool.lease.vo.v2.CoinAndAlgorithmVo;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.m2pool.lease.netty.message.MethodConstant.MINING_RAG;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author yyb
 * @since 2025-07-23
 */
@Service
public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper, LeaseOrderInfo> implements LeaseOrderInfoService {


    @Resource
    private LeaseOrderInfoMapper leaseOrderInfoMapper;

    @Resource
    private LeaseProductMachineMapper leaseProductMachineMapper;

    @Resource
    private LeaseOrderItemService leaseOrderItemService;

    @Resource
    private LeaseOrderItemMapper leaseOrderItemMapper;

    @Resource
    private LeaseShopMapper leaseShopMapper;

    @Resource
    private LeasePaymentRecordMapper leasePaymentRecordMapper;

    @Resource
    private LeaseShoppingCartInfoMapper leaseShoppingCartInfoMapper;

    @Resource
    private LeaseProductMapper leaseProductMapper;

    @Resource
    private LeaseProductService leaseProductService;

    @Resource
    private LeaseUserWalletDataMapper leaseUserWalletDataMapper;

    @Resource
    private LeaseUserOwnedProductService leaseUserOwnedProductService;

    @Resource
    private LeaseUserMapper leaseUserMapper;

    @Resource
    private LeaseProductMachinePriceMapper leaseProductMachinePriceMapper;

    @Resource
    private LeaseOrderMiningMapper leaseOrderMiningMapper;

    @Resource
    private RedisService redisService;



    @Override
    @DSTransactional
    public Result<String> addOrders(OrderAndCodeVo orderAndCodeVo ) {
        String userEmail = SecurityUtils.getUsername();
        GoogleInfo googleInfo = leaseUserMapper.getGoogleInfoByEmail(userEmail);
        //TODO 开发环境
        //if(googleInfo == null || StringUtils.isBlank(googleInfo.getSecret())){
        //    //未绑定定谷歌验证器
        //    return Result.fail("您的账号未开启双重验证,请先开启验证!");
        //}
        //if(!GoogleAuthenticator.checkCode(googleInfo.getSecret(), orderAndCodeVo.getCode(), System.currentTimeMillis())){
        //    return Result.fail("谷歌验证码错误");
        //}

        List<OrderInfoVo> orderInfoVoList = orderAndCodeVo.getOrderInfoVoList();

        //存储相同链和币种的map集合
        Map<String, Map<String, List<OrderInfoVo>>> chainAndCoinMap = new HashMap<>();
        Map<Long, LeaseProductMachinePrice> orderTotalPriceGroupByChainAndCoin = leaseProductMachinePriceMapper.getOrderTotalPriceGroupByChainAndCoin(orderInfoVoList);
        //chain + coin 钱包 对应的总价
        Map<String, BigDecimal> totalPriceMap = new HashMap<>();
        //存储根据chain和coin去重后的set集合
        Set<ChainAndCoinDto> chainAndCoinSet = new HashSet<>();
        Set<ChainAndCoinDto> chainAndCoinAndShopIdSet = new HashSet<>();
        //其他
        Map<Long, OrderInfoVo> machineMap = new HashMap<>();
        LocalDateTime now = LocalDateTime.now();
        List<Long> machineIds = new ArrayList<>();
        Set<Long> productIds = new HashSet<>();
        for (OrderInfoVo vo : orderInfoVoList) {
            String chain = vo.getChain();
            String coin = vo.getCoin();
            Long shopId = vo.getShopId();
            String key = chain +"-"+ coin+"-"+shopId;
            chainAndCoinSet.add(new ChainAndCoinDto(coin, chain));
            chainAndCoinAndShopIdSet.add(new ChainAndCoinDto(coin, chain,vo.getShopId()));
            chainAndCoinMap.computeIfAbsent(chain, k -> new HashMap<>());
            Map<String, List<OrderInfoVo>> innerMap = chainAndCoinMap.get(chain);
            innerMap.computeIfAbsent(coin, k -> new ArrayList<>()).add(vo);
            // 获取对应买方本次链和币对应需要支付的总金额
            totalPriceMap.compute(key, (k, v) -> {
                if (v == null) {
                    v = BigDecimal.ZERO;
                }
                LeaseProductMachinePrice priceInfo = orderTotalPriceGroupByChainAndCoin.get(vo.getMachineId());
                if (priceInfo != null) {
                    v = v.add(priceInfo.getPrice().multiply(BigDecimal.valueOf(vo.getLeaseTime())));
                }
                return v;
            });
            machineMap.put(vo.getMachineId(), vo);
            machineIds.add(vo.getMachineId());
            productIds.add(vo.getProductId());
        }
        //获取买家需要进行支付的几个钱包
        List<LeaseUserWalletData> walletDataList = leaseUserWalletDataMapper.selectWalletByChainAndCoinAndUsername(chainAndCoinSet, userEmail);
        if (walletDataList.size() != chainAndCoinSet.size()){
            return Result.fail("下单失败!订单选择的支付方式中对应钱包您还未绑定并充值!");
        }

        Map<String, LeaseUserWalletData> fromAddressMap = walletDataList.stream().collect(Collectors.toMap(item -> item.getFromChain() + "-" + item.getFromSymbol(), Function.identity()));
        //封装订单主表信息
        List<LeaseOrderInfo> leaseOrderInfoList = new ArrayList<>();
        totalPriceMap.forEach((key, totalPrice)->{
            String[] split = key.split("-");
            String coin = split[1];
            String chain = split[0];
            BigDecimal fee = CoinCharge.getFee(chain, coin, totalPrice);
            leaseOrderInfoList.add(LeaseOrderInfo.builder()
                    .chainAndCoinAndShopIdKey(key)
                    .chainAndCoinKey(chain+"-"+coin)
                    .orderNumber(UuidGeneratorUtil.generateUuidWithoutHyphen())
                    .userId(userEmail)
                    .fee(fee)
                    .totalPrice(totalPrice)
                    .createTime(now)
                    .build());
        });

        //删除购物车中矿机
        int delete = leaseShoppingCartInfoMapper.delete(new LambdaUpdateWrapper<LeaseShoppingCartInfo>()
                .eq(LeaseShoppingCartInfo::getUserId, SecurityUtils.getUsername())
                .in(LeaseShoppingCartInfo::getProductMachineId, machineIds));
        if (delete < 1){
            throw new OrderException("生成订单失败,购物车中不存在矿机商品");
        }
        //查找矿机详情信息
        List<LeaseProductMachine> machinesList = leaseProductMachineMapper.selectBatchIds(machineIds);
        for (LeaseProductMachine leaseProductMachine : machinesList) {
            if (leaseProductMachine.getState() == 1 || leaseProductMachine.getDel()){
                throw new OrderException("生成订单失败,订单矿机中存在已下架矿机");
            }
        }
        // 获取卖家店铺钱包配置
        List<LeaseShopConfig> payAddressAndPayCoin = leaseShopMapper.getPayAddressAndPayCoin(chainAndCoinAndShopIdSet);
        Map<String, LeaseShopConfig> configMap = new HashMap<>();
        Map<String, Set<String>> feeMap = new HashMap<>();
        for (LeaseShopConfig leaseShopConfig : payAddressAndPayCoin) {
            String key = leaseShopConfig.getChain() + "-" + leaseShopConfig.getPayCoin() +"-"+leaseShopConfig.getShopId();
            configMap.put(key, leaseShopConfig);
            feeMap.computeIfAbsent(leaseShopConfig.getChain() + "-" + leaseShopConfig.getPayCoin(), k -> new HashSet<>()).add(leaseShopConfig.getPayAddress());
        }
        boolean save = saveBatch(leaseOrderInfoList);
        if (!save){
            return Result.fail("生成订单失败");
        }
        Map<String, LeaseOrderInfo> orderInfoMap = leaseOrderInfoList.stream()
                .collect(Collectors.toMap(LeaseOrderInfo::getChainAndCoinAndShopIdKey, Function.identity()));

        //订单详情表业务
        List<LeaseProduct> leaseProducts = leaseProductMapper.selectBatchIds(productIds);
        Map<Long, LeaseProduct> productMap = leaseProducts.stream().collect(Collectors.toMap(LeaseProduct::getId, Function.identity()));
        List<LeaseOrderItem> leaseOrderItems = new ArrayList<>();

        for (LeaseProductMachine leaseProductMachine : machinesList) {
            Long machineId = leaseProductMachine.getId();
            OrderInfoVo orderInfoVo = machineMap.get(machineId);
            String chain = orderInfoVo.getChain();
            String coin = orderInfoVo.getCoin();
            Long shopId = orderInfoVo.getShopId();
            String key = chain + "-" + coin + "-" + shopId;
            LeaseProductMachinePrice leaseProductMachinePrice = orderTotalPriceGroupByChainAndCoin.get(machineId);
            LeaseShopConfig leaseShopConfig = configMap.get(key);
            LeaseUserWalletData walletInfo = fromAddressMap.get(chain + "-" + coin);
            if (leaseShopConfig == null){
                throw new OrderException("选择的卖家收款钱包不存在,请重行选择支付方法");
            }
            LeaseOrderInfo leaseOrderInfo = orderInfoMap.get(key);
            //设置销售数量
            LeaseProduct leaseProduct = productMap.get(leaseProductMachine.getProductId());
            leaseProduct.setSaleNumber(leaseProduct.getSaleNumber() + 1);
            leaseOrderItems.add( LeaseOrderItem.builder()
                    .userId(userEmail)
                    .orderId(leaseOrderInfo.getId())
                    .productId(leaseProductMachine.getProductId())
                    .productMachineId(leaseProductMachine.getId())
                    .price(leaseProductMachinePrice.getPrice())
                    .user(leaseProductMachine.getUser())
                    .miner(leaseProductMachine.getMiner())
                    .theoryIncome(leaseProductMachine.getTheoryIncome().multiply(BigDecimal.valueOf(orderInfoVo.getLeaseTime())))
                    .coin(leaseProduct.getCoin())
                    .leaseTime(orderInfoVo.getLeaseTime())
                    .payCoin(leaseShopConfig.getPayCoin())
                    .address(leaseShopConfig.getPayAddress())
                    .chain(leaseShopConfig.getChain())
                    .symbol(leaseShopConfig.getPayCoin())
                    .name(leaseProduct.getName())
                    .image(leaseProduct.getImage())
                    .shopId(leaseProductMachine.getShopId())
                    .fromAddress(walletInfo.getFromAddress())
                    .fromChain(walletInfo.getFromChain())
                    .fromSymbol(walletInfo.getFromSymbol())
                    .type(leaseProduct.getType() == 1)
                    .build());
        }

        boolean a = leaseProductService.updateBatchById(leaseProducts);
        boolean b = leaseOrderItemService.saveBatch(leaseOrderItems);
        //新增上述矿机到用户已购机器表中
        boolean c = leaseUserOwnedProductService.afterPaySuccessTodoSomeEvent(leaseOrderItems);

        //开始生成支付订单并返回
        if (a && b && c){
            checkBalanceAndSetBlockBalance(fromAddressMap,leaseOrderInfoList);
            //修改商品矿机售出状态 以商品机器sale_state作为乐观锁
            int i = leaseProductMachineMapper.updateLockState(machineIds);
            if (i != machineIds.size()){
                throw new OrderException("订单中已有商品售出,请刷新购物车删除已售出商品,重新结算生成订单");
            }
            return Result.success("订单生成成功");
        }
        throw new OrderException("订单生成失败");
    }


    /**
     * 检测钱包余额是否充足 余额充足则将余额冻结
     * @return
     */
   public void checkBalanceAndSetBlockBalance(Map<String, LeaseUserWalletData> fromAddressMap,List<LeaseOrderInfo> leaseOrderInfoList){
       Map<String, List<LeaseOrderInfo>> collect = leaseOrderInfoList.stream().collect(Collectors.groupingBy(LeaseOrderInfo::getChainAndCoinKey));

       fromAddressMap.forEach((chainAndCoinKey, leaseUserWalletData) -> {
           List<LeaseOrderInfo> leaseOrderInfos = collect.get(chainAndCoinKey);
           BigDecimal totalAmount = leaseOrderInfos.stream().map(LeaseOrderInfo::getTotalPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
           BigDecimal totalFee = leaseOrderInfos.stream().map(LeaseOrderInfo::getFee).reduce(BigDecimal.ZERO, BigDecimal::add);

           if (leaseUserWalletData.getBalance().subtract(leaseUserWalletData.getBlockedBalance()).compareTo(totalAmount.add(totalFee)) < 0){
               throw new OrderException("下单失败,买家"+chainAndCoinKey+"钱包余额不足,缺少" + (totalAmount.add(totalFee).subtract(leaseUserWalletData.getBalance().subtract(leaseUserWalletData.getBlockedBalance()))) +"满足支付需求");
           }
           leaseUserWalletData.setBlockedBalance(leaseUserWalletData.getBlockedBalance().add(totalAmount).add(totalFee));
           leaseUserWalletDataMapper.updateById(leaseUserWalletData);
       });

   }

    ///**
    // * 获取本次订单需总支付金额
    // *
    // * @param leaseOrderItems
    // * @return
    // */
    //public BigDecimal createPaymentOrders(List<LeaseOrderItem> leaseOrderItems,Map<Long, LeaseShopConfig>  shopConfigMap,LocalDateTime now,LeaseOrderInfo build){
    //    //按店铺id分组
    //    Map<Long, List<LeaseOrderItem>> shopMap = leaseOrderItems.stream().collect(Collectors.groupingBy(LeaseOrderItem::getShopId));
    //    List<PaymentRecordDto> paymentRecordDTOs = new ArrayList<>();
    //    List<LeasePaymentRecord> paymentRecords = shopMap.values().stream()
    //            .map(items -> {
    //                LeaseOrderItem firstItem = items.get(0);
    //                LeaseShopConfig leaseShopConfig = shopConfigMap.get(firstItem.getShopId());
    //                BigDecimal totalAmount = BigDecimal.ZERO;
    //                for (LeaseOrderItem item : items) {
    //                    totalAmount = totalAmount.add(item.getPrice().multiply(BigDecimal.valueOf(item.getLeaseTime())));
    //                }
    //                Long orderId = build.getId();
    //                Long productId = firstItem.getProductId();
    //                String payCoin = firstItem.getPayCoin();
    //                String address = firstItem.getAddress();
    //
    //
    //                paymentRecordDTOs.add(PaymentRecordDto.builder()
    //                        .payCoin(payCoin)
    //                        .amount(totalAmount)
    //                        .payAddress(address)
    //                        .img(leaseShopConfig.getQrcode())
    //                        .build());
    //
    //                return LeasePaymentRecord.builder()
    //                        .orderId(orderId)
    //                        .shopId(firstItem.getShopId())
    //                        .productId(productId)
    //                        .payCoin(payCoin)
    //                        .amount(totalAmount)
    //                        .payAddress(address)
    //                        .qrcode(leaseShopConfig.getQrcode())
    //                        .createTime(now)
    //                        .build();
    //            })
    //            .collect(Collectors.toList());
    //
    //
    //    //4.保存支付订单
    //    //boolean saved = leasePaymentRecordService.saveBatch(paymentRecords);
    //    //List<RabbitmqOrderMessage> rabbitmqOrderMessageList = paymentRecords.stream()
    //    //        .map(paymentRecord -> RabbitmqOrderMessage.builder().orderId(paymentRecord.getOrderId()).shopId(paymentRecord.getShopId()).build())
    //    //        .collect(Collectors.toList());
    //    //
    //    ////5.发送订单mq超时消息
    //    //rabbitTemplate.convertAndSend(
    //    //        ORDER_OVERTIME_EXCHANGE_NAME,
    //    //        ORDER_OVERTIME_ROUTING_KEY,
    //    //        rabbitmqOrderMessageList);
    //
    //
    //
    //
    //    //返回总支付金额
    //    return paymentRecordDTOs.stream().map(PaymentRecordDto::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
    //
    //    //throw new PaymentException("添加支付订单失败");
    //}


    @Override
    public Result<List<PaymentRecordDto>> buyAgain(List<OrderInfoVo> orderInfoVoList) {
        //LocalDateTime now = LocalDateTime.now();
        //List<Long> machineIds = new ArrayList<>();
        //Set<Long> shopIds = new HashSet<>();
        //Set<Long> productIds = new HashSet<>();
        //
        //for (OrderInfoVo vo : orderInfoVoList) {
        //    machineIds.add(vo.getMachineId());
        //    shopIds.add(vo.getShopId());
        //    productIds.add(vo.getProductId());
        //}
        //List<LeaseProductMachine> machinesList = leaseProductMachineMapper.selectBatchIds(machineIds);
        //BigDecimal totalPrice = calculateOrderPrice(orderInfoVoList, machinesList);
        ////获取商铺配置信息（支付地址，支付币种）  目前配置里面的支付地址 改为从leaseShopAddressConfig 获取
        //List<LeaseShopConfig> payAddressAndPayCoin = leaseShopMapper.getPayAddressAndPayCoin(shopIds);
        //Map<Long, LeaseShopConfig> shopConfigMap = payAddressAndPayCoin.stream().collect(Collectors.toMap(LeaseShopConfig::getShopId, Function.identity()));
        //// 这里修改店铺配置的地址为我们自营的地址
        //updatePayAddressForSelf(shopConfigMap);
        ////为商品机器加锁 以商品机器sale_state作为乐观锁
        //int i = leaseProductMachineMapper.updateLockState(machineIds);
        //if (i != machineIds.size()){
        //    throw new ProductSoldOutException("订单中已有商品售出,不能再次购买");
        //}
        ////订单主信息新增
        //String orderNumber = UuidGeneratorUtil.generateUuidWithoutHyphen();
        //LeaseOrderInfo build = LeaseOrderInfo.builder()
        //        .orderNumber(orderNumber)
        //        .userId(SecurityUtils.getUsername())
        //        .totalPrice(totalPrice)
        //        .createTime(now)
        //        .build();
        //boolean save = save(build);
        //if (!save){
        //    return Result.fail("添加订单失败");
        //}
        //
        ////创建订单明细
        //Map<Long, OrderInfoVo> machineMap = orderInfoVoList.stream().collect(Collectors.toMap(OrderInfoVo::getMachineId, Function.identity()));
        //List<LeaseProduct> leaseProducts = leaseProductMapper.selectBatchIds(productIds);
        //Map<Long, LeaseProduct> productMap = leaseProducts.stream().collect(Collectors.toMap(LeaseProduct::getId, Function.identity()));
        //List<LeaseOrderItem> leaseOrderItems = new ArrayList<>();
        //for (LeaseProductMachine leaseProductMachine : machinesList) {
        //    LeaseShopConfig leaseShopConfig = shopConfigMap.get(leaseProductMachine.getShopId());
        //    OrderInfoVo orderInfoVo = machineMap.get(leaseProductMachine.getId());
        //    LeaseProduct leaseProduct = productMap.get(leaseProductMachine.getProductId());
        //    leaseOrderItems.add( LeaseOrderItem.builder()
        //            .orderId(build.getId())
        //            .productId(leaseProductMachine.getProductId())
        //            .productMachineId(leaseProductMachine.getId())
        //            .price(leaseProductMachine.getPrice())
        //            .user(leaseProductMachine.getUser())
        //            .miner(leaseProductMachine.getMiner())
        //            .theoryIncome(leaseProductMachine.getTheoryIncome().multiply(BigDecimal.valueOf(orderInfoVo.getLeaseTime())))
        //            .coin(leaseProduct.getCoin())
        //            .leaseTime(orderInfoVo.getLeaseTime())
        //            .payCoin(leaseShopConfig.getPayCoin())
        //            .address(leaseShopConfig.getPayAddress())
        //            .name(leaseProduct.getName())
        //            .image(leaseProduct.getImage())
        //            .shopId(leaseProductMachine.getShopId())
        //            .build());
        //}
        //boolean b = leaseOrderItemService.saveBatch(leaseOrderItems);
        ////开始生成支付订单并返回
        //if (b){
        //    return Result.success(createPaymentOrders(leaseOrderItems, shopConfigMap, now, build));
        //}
        //return Result.fail("生成支付订单失败");
        return null;
    }

    @Override
    public PageResult<OrderInfoDto> getOrdersByStatus(OrderInfoStateVo orderInfoStateVo) {
        PageHelper.startPage(orderInfoStateVo.getPageNum(), orderInfoStateVo.getPageSize());

        //获取订单信息
        List<OrderInfoDto> ordersByStatus = leaseOrderInfoMapper.getOrdersByStatus(SecurityUtils.getUsername(),orderInfoStateVo.getStatus());
        PageInfo<OrderInfoDto> pageInfo = new PageInfo<>(ordersByStatus);
        List<Long> ids = ordersByStatus.stream().map(OrderInfoDto::getId).collect(Collectors.toList());
        if (ids.isEmpty()){
            return PageResult.success(new ArrayList<>());
        }
        //获取订单对应的订单详情列表
        List<LeaseOrderItem> leaseOrderItems = leaseOrderItemMapper.selectList(new QueryWrapper<LeaseOrderItem>()
                .in("order_id",ids));

        //设置订单详情集合，已支付金额，待支付金额
        setPayAmountAndItems(leaseOrderItems,ordersByStatus,ids);

        PageResult<OrderInfoDto> success = PageResult.success(ordersByStatus);
        success.setTotal(pageInfo.getTotal());
        success.setTotalPage(pageInfo.getPages());
        PageHelper.clearPage();
        return success;
    }



    @Override
    public PageResult<OrderInfoDto> getOrdersByStatusForSeller(OrderInfoStateVo orderInfoStateVo) {
        LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper<LeaseShop>()
                .eq(LeaseShop::getUserEmail, SecurityUtils.getUsername()));
        if (leaseShop == null){
            //店铺不存在
            return PageResult.success(new ArrayList<>());
        }
        //查询到商铺的所有订单
        List<LeaseOrderItem> orderItemList = leaseOrderItemMapper.selectList(new LambdaQueryWrapper<LeaseOrderItem>()
                .eq(LeaseOrderItem::getShopId, leaseShop.getId()));
        if (orderItemList.isEmpty()){
            //订单不存在
            return PageResult.success(new ArrayList<>());
        }
        List<Long> orderInfoIds = orderItemList.stream().map(LeaseOrderItem::getOrderId).distinct().collect(Collectors.toList());
        PageHelper.startPage(orderInfoStateVo.getPageNum(), orderInfoStateVo.getPageSize());
        //获取订单信息
        List<OrderInfoDto> ordersByStatus = leaseOrderInfoMapper.getOrdersByStatusForSeller(orderInfoStateVo.getStatus(), orderInfoIds);
        PageInfo<OrderInfoDto> pageInfo = new PageInfo<>(ordersByStatus);
        setPayAmountAndItems(orderItemList,ordersByStatus,orderInfoIds);
        PageResult<OrderInfoDto> success = PageResult.success(ordersByStatus);
        success.setTotal(pageInfo.getTotal());
        success.setTotalPage(pageInfo.getPages());
        PageHelper.clearPage();
        return success;

    }

    /**
     * 遍历订单详情集合。统计 已支付金额，待支付金额
     * @param leaseOrderItems
     * @param ordersByStatus
     * @param ids
     */
    public void setPayAmountAndItems(List<LeaseOrderItem> leaseOrderItems,List<OrderInfoDto> ordersByStatus,List<Long> ids){
        Map<Long, BigDecimal> hasPayAmountMap = new HashMap<>();
        Map<Long, BigDecimal> hasPayRealAmountMap = new HashMap<>();
        Map<Long, List<OrderItemDto>> orderItemDtoMap = new HashMap<>();
        for (LeaseOrderItem leaseOrderItem : leaseOrderItems) {
            orderItemDtoMap.computeIfAbsent(leaseOrderItem.getOrderId(), k -> new ArrayList<>()).add( OrderItemDto.builder()
                    .shopId(leaseOrderItem.getShopId())
                    .orderId(leaseOrderItem.getOrderId())
                    .productId(leaseOrderItem.getProductId())
                    .productMachineId(leaseOrderItem.getProductMachineId())
                    .leaseTime(leaseOrderItem.getLeaseTime())
                    .address(leaseOrderItem.getAddress())
                    .payCoin(leaseOrderItem.getPayCoin())
                    .name(leaseOrderItem.getName())
                    .price(leaseOrderItem.getPrice())
                    .image(leaseOrderItem.getImage())
                    .numbers(leaseOrderItem.getNumbers())
                    .type(leaseOrderItem.getType() ? 1 : 0)
                    .build());
            // 累加支付金额
            hasPayAmountMap.merge(leaseOrderItem.getOrderId(), leaseOrderItem.getAlreadyPayAmount(), BigDecimal::add);
            // 累加实际支付金额
            hasPayRealAmountMap.merge(leaseOrderItem.getOrderId(), leaseOrderItem.getAlreadyPayRealAmount(), BigDecimal::add);
        }


        ordersByStatus.forEach(orderInfoDto -> {
            List<OrderItemDto> orderItems = orderItemDtoMap.get(orderInfoDto.getId());
            BigDecimal hasPayAmount = hasPayAmountMap.get(orderInfoDto.getId());
            BigDecimal hasPayRealAmount = hasPayRealAmountMap.get(orderInfoDto.getId());
            orderInfoDto.setOrderItemDtoList(orderItems);
            orderInfoDto.setNoPayAmount(orderInfoDto.getTotalPrice().subtract(hasPayAmount).doubleValue());
            orderInfoDto.setPayAmount(hasPayRealAmount.doubleValue()+"/"+hasPayAmount.doubleValue());
        });
    }

    @Override
    public Result<OrderInfoDto> getOrdersByIds(OrderVo orderVo) {

        //获取订单主信息
        LeaseOrderInfo leaseOrderInfo = leaseOrderInfoMapper.selectById(orderVo.getOrderId());
        if (leaseOrderInfo == null){
            return Result.fail("未找到该订单");
        }
        //获取订单对应的订单详情列表
        List<LeaseOrderItem> leaseOrderItems = leaseOrderItemMapper.selectList(new LambdaQueryWrapper<LeaseOrderItem>()
                .eq(LeaseOrderItem::getOrderId, orderVo.getOrderId()));

        List<OrderItemDto> collect1 = leaseOrderItems.stream().map(leaseOrderItem -> OrderItemDto.builder()
                .orderId(leaseOrderItem.getOrderId())
                .productId(leaseOrderItem.getProductId())
                .productMachineId(leaseOrderItem.getProductMachineId())
                .leaseTime(leaseOrderItem.getLeaseTime())
                .address(leaseOrderItem.getAddress())
                .payCoin(leaseOrderItem.getPayCoin())
                .name(leaseOrderItem.getName())
                .image(leaseOrderItem.getImage())
                .price(leaseOrderItem.getPrice())
                .type(leaseOrderItem.getType() ? 1 : 0)
                .build()).collect(Collectors.toList());

        OrderInfoDto orderInfoDto = OrderInfoDto.builder()
                .id(leaseOrderInfo.getId())
                .shopName(leaseOrderInfo.getShopName())
                .orderNumber(leaseOrderInfo.getOrderNumber())
                .userId(leaseOrderInfo.getUserId())
                .totalPrice(leaseOrderInfo.getTotalPrice())
                .status(leaseOrderInfo.getStatus())
                .createTime(leaseOrderInfo.getCreateTime())
                .endTime(leaseOrderInfo.getUpdateTime())
                .orderItemDtoList(collect1)
                .build();

        return Result.success(orderInfoDto);
    }


    @Override
    public Result<String> cancelOrder(OrderVo orderVo) {
        int i = leaseOrderInfoMapper.updateById(LeaseOrderInfo.builder().id(orderVo.getOrderId()).status(2).build());
        if (i > 0){
            //删除支付订单
            int update = leasePaymentRecordMapper.update(LeasePaymentRecord.builder().del(false).build(), new LambdaQueryWrapper<LeasePaymentRecord>().eq(LeasePaymentRecord::getOrderId, orderVo.getOrderId()));
            return Result.success("取消订单成功");
        }
        return Result.fail("取消订单失败");
    }


    @Override
    public Result<BigDecimal> getCoinPrice(CoinVo coinVo) {
        BigDecimal coinPrice = getCoinPrice(coinVo.getCoin());
        return Result.success(coinPrice);
    }
    public BigDecimal getCoinPrice(String coin) {
        coin = coin.toLowerCase();
        String priceKey = RedisCoinInfoKey.getPiceKey(coin);
        if (priceKey == null){
            return BigDecimal.ZERO;
        }
        BigDecimal price = redisService.getCacheBigDecimal(priceKey);
        if (price != null){
            return price;
        }
        price = leaseOrderInfoMapper.getCoinPrice(coin);
        //从数据库中获取最后一次存储的币价（可能不够实时）
        if (price == null){
            return BigDecimal.ZERO;
        }
        return price;
    }

    @Resource
    private LeaseMachinePriceMapper leaseMachinePriceMapper;

    @Resource
    private LeaseMachineMapper leaseMachineMapper;

    @Resource
    private LeaseMachineConfigMapper leaseMachineConfigMapper;


    @Override
    public Result<List<CoinAndAlgorithmListDto>> getMachineSupportCoinAndAlgorithm(List<BaseVo> machineIds) {
        //lease_pool_coin_config 中获取本系统支持的币种和算法
        List<CoinAndAlgorithmDto> machineSupportCoinAndAlgorithm = leaseMachineConfigMapper.getMachineSupportCoinAndAlgorithm(machineIds);
        // 按币种分组
        Map<String, List<CoinAndAlgorithmDto>> coinGroup = machineSupportCoinAndAlgorithm.stream()
                .collect(Collectors.groupingBy(CoinAndAlgorithmDto::getCoin));
        List<CoinAndAlgorithmListDto> result = new ArrayList<>();
        // 遍历每个币种
        coinGroup.forEach((coin, dtos) -> {
            CoinAndAlgorithmListDto item = new CoinAndAlgorithmListDto();
            item.setCoin(coin);
            // 转换算法列表
            List<CoinAndAlgorithmListDto.AlgorithmListDto> algorithmList = dtos.stream()
                    .map(dto -> {
                        CoinAndAlgorithmListDto.AlgorithmListDto algoDto = new CoinAndAlgorithmListDto.AlgorithmListDto();
                        algoDto.setAlgorithm(dto.getAlgorithm());

                        // 转换机器ID列表
                        List<Long> ids = Arrays.stream(dto.getMachineIds().split(","))
                                .map(Long::valueOf)
                                .collect(Collectors.toList());
                        algoDto.setMachineIds(ids);

                        return algoDto;
                    })
                    .collect(Collectors.toList());

            item.setAlgorithmList(algorithmList);
            result.add(item);
        });
        return Result.success(result);
    }


    @Override
    public Result<List<MiningInfoDto>> getMachineSupportPool(CoinAndAlgorithmVo coinAndAlgorithmVo) {
        List<MiningConfigSelectDto> coinConfigList = leaseMachineConfigMapper.getConfigList(coinAndAlgorithmVo.getCoin(), coinAndAlgorithmVo.getAlgorithm());
        // 将查询结果按poolId分组并转换为MiningInfoDto列表
        List<MiningInfoDto> miningInfoList = coinConfigList.stream()
                .collect(Collectors.groupingBy(MiningConfigSelectDto::getPoolId))
                .entrySet().stream()
                .map(entry -> {
                    MiningConfigSelectDto firstConfig = entry.getValue().get(0);

                    List<MiningCoinInfoDto> coinInfoList = entry.getValue().stream()
                            .map(config -> MiningCoinInfoDto.builder()
                                    .coinConfigId(config.getCoinConfigId())
                                    .coin(config.getCoin())
                                    .algorithm(config.getAlgorithm())
                                    .miningTcpGpuUrl(config.getMiningTcpGpuUrl())
                                    .modelName(config.getModelName())
                                    .modelFee(config.getModelFee())
                                    .walletMining(config.getWalletMining())
                                    .build())
                            .collect(Collectors.toList());

                    return MiningInfoDto.builder()
                            .poolId(entry.getKey())
                            .poolName(firstConfig.getPoolName())
                            .miningCoinInfoList(coinInfoList)
                            .build();
                })
                .collect(Collectors.toList());
        return Result.success(miningInfoList);
    }

    @Override
    @DSTransactional
    public Result<String> addOrdersV2(OrderAndCodeVo orderAndCodeVo) {
        String userEmail = SecurityUtils.getUsername();
        List<OrderInfoVo> orderInfoVoList = orderAndCodeVo.getOrderInfoVoList();
        List<OrderMiningInfoDto> orderMiningInfoDtoList = orderAndCodeVo.getOrderMiningInfoDtoList();

        String errors = checkMiningMinersExistAndAddress(orderMiningInfoDtoList);
        if (StringUtils.isNotEmpty(errors)){
            return Result.fail(errors);
        }
        Map<Long, OrderMiningInfoDto> userAndMinerMap = orderMiningInfoDtoList.stream().collect(Collectors.toMap(OrderMiningInfoDto::getMachineId, Function.identity()));
        GoogleInfo googleInfo = leaseUserMapper.getGoogleInfoByEmail(userEmail);
        //TODO 开发环境
        //if(googleInfo == null || StringUtils.isBlank(googleInfo.getSecret())){
        //    //未绑定定谷歌验证器
        //    return Result.fail("您的账号未开启双重验证,请先开启验证!");
        //}
        //if(!GoogleAuthenticator.checkCode(googleInfo.getSecret(), orderAndCodeVo.getCode(), System.currentTimeMillis())){
        //    return Result.fail("谷歌验证码错误");
        //}
        Map<Long, OrderInfoVo> collect1 = orderInfoVoList.stream()
                .collect(Collectors.toMap(OrderInfoVo::getMachineId,Function.identity()));
        List<LeaseMachine> machineList = leaseMachineMapper.selectBatchIds(collect1.keySet());
        Map<Boolean, List<LeaseMachine>> machineTypeMap = machineList.stream().collect(Collectors.groupingBy(LeaseMachine::getType));
        List<LeaseMachine> gpuMachines = machineTypeMap.get(true) == null ? new ArrayList<>() : machineTypeMap.get(true);
        List<LeaseMachine> asicMachines = machineTypeMap.get(false) == null ? new ArrayList<>() : machineTypeMap.get(false);
        //校验asic 可售数量是否充足 + gpu 机器对应客户端是否在线
        boolean isAsicPass = checkAsicMachine(asicMachines,collect1);
        if (!isAsicPass){
            return Result.fail("购买的ASIC商品中,存在可售数量不足的商品!");
        }
        boolean isGpuPass = checkGpuMachine(gpuMachines);
        //TODO 开发环境
        //if (!isGpuPass){
        //    return Result.fail("购买的GPU商品中,存在客户端不在线的矿机!");
        //}
        //存储相同链和币种的map集合
        Map<String, Map<String, List<OrderInfoVo>>> chainAndCoinMap = new HashMap<>();
        Map<Long, LeaseMachinePrice> orderTotalPriceGroupByChainAndCoin = leaseMachinePriceMapper.getOrderTotalPriceGroupByChainAndCoin(orderInfoVoList);
        //chain + coin 钱包 对应的总价
        Map<String, BigDecimal> totalPriceMap = new HashMap<>();
        //存储根据chain和coin去重后的set集合
        Set<ChainAndCoinDto> chainAndCoinSet = new HashSet<>();
        Set<ChainAndCoinDto> chainAndCoinAndShopIdSet = new HashSet<>();
        //其他
        Map<Long, OrderInfoVo> machineMap = new HashMap<>();
        LocalDateTime now = LocalDateTime.now();
        long startTime = System.currentTimeMillis();
        List<Long> machineIds = new ArrayList<>();
        Set<Long> shopIds = new HashSet<>();
        for (OrderInfoVo vo : orderInfoVoList) {
            String chain = vo.getChain();
            String coin = vo.getCoin();
            Long shopId = vo.getShopId();
            String key = chain +"-"+ coin+"-"+shopId;
            chainAndCoinSet.add(new ChainAndCoinDto(coin, chain));
            chainAndCoinAndShopIdSet.add(new ChainAndCoinDto(coin, chain,vo.getShopId()));
            chainAndCoinMap.computeIfAbsent(chain, k -> new HashMap<>());
            Map<String, List<OrderInfoVo>> innerMap = chainAndCoinMap.get(chain);
            innerMap.computeIfAbsent(coin, k -> new ArrayList<>()).add(vo);
            // 获取对应买方本次链和币对应需要支付的总金额
            totalPriceMap.compute(key, (k, v) -> {
                if (v == null) {
                    v = BigDecimal.ZERO;
                }
                LeaseMachinePrice priceInfo = orderTotalPriceGroupByChainAndCoin.get(vo.getMachineId());
                if (priceInfo != null) {
                    v = v.add(priceInfo.getPrice().multiply(BigDecimal.valueOf(vo.getNumbers())).multiply(BigDecimal.valueOf(vo.getLeaseTime())));
                }
                return v;
            });
            machineMap.put(vo.getMachineId(), vo);
            machineIds.add(vo.getMachineId());
            shopIds.add(vo.getShopId());
        }
        //店铺名
        Map<Long, LeaseShop> shopNameMapByIds = leaseShopMapper.getShopNameMapByIds(new ArrayList<>(shopIds));
        //获取买家需要进行支付的几个钱包
        List<LeaseUserWalletData> walletDataList = leaseUserWalletDataMapper.selectWalletByChainAndCoinAndUsername(chainAndCoinSet, userEmail);
        if (walletDataList.size() != chainAndCoinSet.size()){
            return Result.fail("下单失败!订单选择的支付方式中对应钱包您还未绑定并充值!");
        }

        Map<String, LeaseUserWalletData> fromAddressMap = walletDataList.stream().collect(Collectors.toMap(item -> item.getFromChain() + "-" + item.getFromSymbol(), Function.identity()));
        //封装订单主表信息
        List<LeaseOrderInfo> leaseOrderInfoList = new ArrayList<>();
        totalPriceMap.forEach((key, totalPrice)->{
            String[] split = key.split("-");
            String coin = split[1];
            String chain = split[0];
            BigDecimal fee = CoinCharge.getFee(chain, coin, totalPrice);
            leaseOrderInfoList.add(LeaseOrderInfo.builder()
                    .chainAndCoinAndShopIdKey(key)
                    .chainAndCoinKey(chain+"-"+coin)
                    .orderNumber(UuidGeneratorUtil.generateUuidWithoutHyphen())
                    .userId(userEmail)
                    .shopName(shopNameMapByIds.get(Long.parseLong(split[2])).getName())
                    .fee(fee)
                    .totalPrice(totalPrice)
                    .createTime(now)
                    .build());
        });

        //删除购物车中矿机
        int delete = leaseShoppingCartInfoMapper.delete(new LambdaUpdateWrapper<LeaseShoppingCartInfo>()
                .eq(LeaseShoppingCartInfo::getUserId, SecurityUtils.getUsername())
                .eq(LeaseShoppingCartInfo::getVersion, 1)
                .in(LeaseShoppingCartInfo::getProductMachineId, machineIds));
        if (delete < 1){
            throw new OrderException("生成订单失败,购物车中不存在矿机商品");
        }
        //查找矿机详情信息
        List<LeaseMachine> machinesList = leaseMachineMapper.selectBatchIds(machineIds);
        List<LeaseMachine> collect = machinesList.stream().filter(leaseMachine -> leaseMachine.getState() || leaseMachine.getDel()).collect(Collectors.toList());
        if (!collect.isEmpty()){
            throw new OrderException("生成订单失败,订单矿机中存在已下架矿机");
        }
        // 获取卖家店铺钱包配置
        List<LeaseShopConfig> payAddressAndPayCoin = leaseShopMapper.getPayAddressAndPayCoin(chainAndCoinAndShopIdSet);
        Map<String, LeaseShopConfig> configMap = new HashMap<>();
        Map<String, Set<String>> feeMap = new HashMap<>();
        for (LeaseShopConfig leaseShopConfig : payAddressAndPayCoin) {
            String key = leaseShopConfig.getChain() + "-" + leaseShopConfig.getPayCoin() +"-"+leaseShopConfig.getShopId();
            configMap.put(key, leaseShopConfig);
            feeMap.computeIfAbsent(leaseShopConfig.getChain() + "-" + leaseShopConfig.getPayCoin(), k -> new HashSet<>()).add(leaseShopConfig.getPayAddress());
        }
        boolean save = saveBatch(leaseOrderInfoList);
        if (!save){
            return Result.fail("生成订单失败");
        }
        Map<String, LeaseOrderInfo> orderInfoMap = leaseOrderInfoList.stream()
                .collect(Collectors.toMap(LeaseOrderInfo::getChainAndCoinAndShopIdKey, Function.identity()));

        //订单详情表业务
        List<LeaseShop> shopList = leaseShopMapper.selectBatchIds(shopIds);
        Map<Long, LeaseShop> shopMap = shopList.stream().collect(Collectors.toMap(LeaseShop::getId, Function.identity()));

        //获取理论算力
        List<MachineHashRateMapDto> theoryHashRate1 = leaseMachineConfigMapper.getTheoryHashRate(orderMiningInfoDtoList);
        Map<Long, List<MachineHashRateMapDto>> theoryHashRateMap = theoryHashRate1.stream()
                .collect(Collectors.groupingBy(MachineHashRateMapDto::getMachineId));

        Map<Long,Long> endMiningMap = new HashMap<>();
        Map<Long,Long> machineOrderIdMap = new HashMap<>();
        Map<Long,Long> machineOrderItemIdMap = new HashMap<>();
        for (LeaseMachine leaseMachine : machinesList) {
            Long machineId = leaseMachine.getId();
            OrderInfoVo orderInfoVo = machineMap.get(machineId);
            List<MachineHashRateMapDto> machineHashRateMapDtos = theoryHashRateMap.get(machineId);
            BigDecimal theoryHashRate = machineHashRateMapDtos.isEmpty() ? BigDecimal.ZERO : machineHashRateMapDtos.get(0).getHashRate();
            String chain = orderInfoVo.getChain();
            String coin = orderInfoVo.getCoin();
            Long shopId = orderInfoVo.getShopId();
            String key = chain + "-" + coin + "-" + shopId;
            LeaseMachinePrice leaseMachinePrice = orderTotalPriceGroupByChainAndCoin.get(machineId);
            LeaseShopConfig leaseShopConfig = configMap.get(key);
            LeaseUserWalletData walletInfo = fromAddressMap.get(chain + "-" + coin);
            if (leaseShopConfig == null){
                throw new OrderException("选择的卖家收款钱包不存在,请重行选择支付方法");
            }
            LeaseOrderInfo leaseOrderInfo = orderInfoMap.get(key);
            //设置销售数量
            LeaseShop leaseShop = shopMap.get(leaseMachine.getShopId());
            leaseShop.setSaleNumber(leaseShop.getSaleNumber() + orderInfoVo.getNumbers());

            LeaseOrderItem build = LeaseOrderItem.builder()
                    .userId(userEmail)
                    .orderId(leaseOrderInfo.getId())
                    .productMachineId(machineId)
                    .price(leaseMachinePrice.getPrice())
                    .user(userAndMinerMap.get(machineId).getPoolUser())
                    .miner(userAndMinerMap.get(machineId).getWorkerId())
                    //.theoryIncome(leaseMachine.getTheoryIncome().multiply(BigDecimal.valueOf(orderInfoVo.getLeaseTime())))
                    .leaseTime(orderInfoVo.getLeaseTime())
                    .payCoin(leaseShopConfig.getPayCoin())
                    .address(leaseShopConfig.getPayAddress())
                    .name(leaseMachine.getHostMac())
                    .chain(leaseShopConfig.getChain())
                    .symbol(leaseShopConfig.getPayCoin())
                    .shopId(leaseMachine.getShopId())
                    .fromAddress(walletInfo.getFromAddress())
                    .fromChain(walletInfo.getFromChain())
                    .fromSymbol(walletInfo.getFromSymbol())
                    .numbers(orderInfoVo.getNumbers())
                    .type(leaseMachine.getType())
                    .theoryPower(theoryHashRate)
                    .build();
            Long timestamp = startTime + orderInfoVo.getLeaseTime() * 24 * 60 * 60 * 1000L;
            endMiningMap.put(machineId,timestamp);
            machineOrderIdMap.put(machineId,leaseOrderInfo.getId());

            leaseOrderItemService.save(build);

            //获取itemId
            machineOrderItemIdMap.put(machineId,build.getId());
        }
        //开始生成支付订单并返回
        checkBalanceAndSetBlockBalance(fromAddressMap,leaseOrderInfoList);
        int i = 0;
        //修改GPU 矿机售出状态为 1 已售出
        if (!gpuMachines.isEmpty()){
            int gpuUpdate = leaseMachineMapper.updateLockState(gpuMachines);
            i += gpuUpdate;
        }
        //修改ASIC 矿机出售数量
        if (!asicMachines.isEmpty()){
            int asicUpdate = leaseMachineMapper.updateLockNumbers(asicMachines);
            i += asicUpdate;
        }
        if (i != machineIds.size()){
            throw new OrderException("订单中已有商品售出,请刷新购物车删除已售出商品,重新结算生成订单");
        }
        //TODO 开发环境 不添加
        //if(!checkGpuMachine(gpuMachines)){
        //    throw new OrderException("购买的GPU商品中,存在客户端不在线的矿机!");
        //}
        //发送消息
        sendMessageToClientAndInsertPurchasedMachine(userEmail,gpuMachines,orderMiningInfoDtoList,endMiningMap,machineOrderIdMap,machineOrderItemIdMap);
        return Result.success("订单生成成功");
    }

    /**
     * 检测 gpu客户端是否在线
     * @param gpuMachines 购买的GPU矿机信息
     */
    private boolean checkGpuMachine(List<LeaseMachine> gpuMachines){
        for (LeaseMachine gpuMachine : gpuMachines) {
            if (ChannelManager.getChannelById(gpuMachine.getHostMac()) == null){
                return false;
            }
        }
        return true;
    }

    /**
     * 校验 ASIC 矿机可售数量是否充足
     *  @param  asicMachines 购买的ASIC矿机信息
     */
    private boolean checkAsicMachine(List<LeaseMachine> asicMachines,Map<Long, OrderInfoVo> numbers){
        for (LeaseMachine leaseMachine : asicMachines) {
            Integer number = numbers.get(leaseMachine.getId()).getNumbers();
            Integer lockNumbers = leaseMachine.getSaleOutNumbers();
            leaseMachine.setLockNumbers(lockNumbers);
            leaseMachine.setSaleOutNumbers(lockNumbers + number);
            if (leaseMachine.getSaleNumbers() - leaseMachine.getSaleOutNumbers() < number){
                return false;
            }
        }
        return true;
    }


    /**
     * 检测本次输入的钱包对应矿工号，是否已经存在并在挖矿中
     * @param orderMiningInfoDtoList
     */
   private String checkMiningMinersExistAndAddress(List<OrderMiningInfoDto> orderMiningInfoDtoList){
       List<OrderMiningInfoDto> orderMiningInfoDtos = leaseOrderMiningMapper.checkMiningMinersExist(orderMiningInfoDtoList);

       StringBuilder errors = new StringBuilder();
       String addressVerify = "";
       List<OrderMiningInfoDto> addressInfos = new ArrayList();
       if(!orderMiningInfoDtos.isEmpty()){
           Map<String, List<OrderMiningInfoDto>> collect = orderMiningInfoDtos.stream().collect(Collectors.groupingBy(OrderMiningInfoDto::getWalletAddress));
           collect.forEach((address,list)->{
               errors.append("钱包地址: ").append(address).append("-已存在挖矿中矿工号");
               for (OrderMiningInfoDto orderMiningInfoDto : list) {
                   errors.append(orderMiningInfoDto.getWorkerId()).append(" ");
               }
               errors.append("请重新输入矿工号\n");
               addressInfos.add(list.get(0));
           });
       }

       if (StringUtils.isNotEmpty(errors.toString())){
           return errors.toString();
       }
       for (OrderMiningInfoDto addressInfo : addressInfos) {
          if (!WalletRuleCheckUtils.checkAddress(addressInfo.getCoin(),addressInfo.getWalletAddress())){
              addressVerify = addressInfo.getWalletAddress() + "钱包地址格式错误";
              break;
          }
       }
       return addressVerify;
   }

    /**
     *  GPU矿机的通过mac发送给相应的客户端
     */
    public void sendMessageToClientAndInsertPurchasedMachine(String userEmail, List<LeaseMachine> gpuMachines, List<OrderMiningInfoDto> orderMiningInfo, Map<Long,Long> endMiningMap, Map<Long,Long> machineOrderIdMap,Map<Long,Long> machineOrderItemIdMap){

        List<Long> configIds = orderMiningInfo.stream()
                .map(OrderMiningInfoDto::getCoinConfigId).distinct().collect(Collectors.toList());
        Map<Long,MiningConfigDto> coinAndAlgoMap  = leaseMachineMapper.getCoinAndAlgoById(configIds);
        Map<Long, LeaseMachine> idAndMacMap = gpuMachines.stream().collect(Collectors
                .toMap(LeaseMachine::getId, Function.identity()));
        Map<String,ClientMessage<MiningConfigDto>> macMessageMap = new HashMap<>();
        List<LeaseOrderMining> orderMiningList = new ArrayList<>();

        for (OrderMiningInfoDto orderMiningInfoDto : orderMiningInfo) {
            MiningConfigDto miningConfigDto = coinAndAlgoMap.get(orderMiningInfoDto.getCoinConfigId());
            Long machineId = orderMiningInfoDto.getMachineId();
            LeaseMachine leaseMachine = idAndMacMap.get(machineId);
            Long orderId = machineOrderIdMap.get(machineId);
            Long endTime = endMiningMap.get(machineId);
            Long orderItemId = machineOrderItemIdMap.get(machineId);
            String poolName = orderMiningInfoDto.getPoolName();
            String walletAddress = orderMiningInfoDto.getWalletAddress();
            String algo = miningConfigDto.getAlgo();
            String coin = miningConfigDto.getCoin();
            String poolUrl = miningConfigDto.getPool();
            String poolUser = orderMiningInfoDto.getPoolUser();
            String poolWorkerId = orderMiningInfoDto.getWorkerId();
            //这里因为orderMiningInfo 存在ASIC 的信息可能为空
            if (leaseMachine != null){
                String mac = leaseMachine.getHostMac();
                ClientMessage<MiningConfigDto> message = new ClientMessage<>();
                MiningConfigDto build = MiningConfigDto.builder()
                        .algo(algo)
                        .coin(coin)
                        .pool(poolName)
                        .pool_url(poolUrl)
                        .wallet_mining(orderMiningInfoDto.getWalletMining())
                        .wallet_address(walletAddress)
                        .pool_user(poolUser)
                        .worker_id(poolWorkerId)
                        .end_timestamp(endTime / 1000).build();
                message.setId(mac);
                message.setMethod(MINING_RAG);
                message.setParams( build);
                //TCP 消息
                macMessageMap.put(mac,message);
            }
            LeaseOrderMining build = LeaseOrderMining.builder()
                    .orderId(orderId)
                    .orderItemId(orderItemId)
                    .machineId(machineId)
                    .userId(userEmail)
                    .coin(coin)
                    .algorithm(algo)
                    .poolUrl(poolUrl)
                    .pool(poolName)
                    .poolUser(poolUser)
                    .workerId(poolWorkerId)
                    .walletAddress(walletAddress)
                    .startTime(LocalDateTime.now())
                    .endTime(DateUtils.toLocalDateTime(new Date(endTime)))
                    .build();
            //已购商品信息
            orderMiningList.add(build);
            long timeout = ChronoUnit.HOURS.between(build.getStartTime(), build.getEndTime().plusHours(1));
            redisService.setCacheObject7(combinationKey(poolName,walletAddress,coin,algo),"1",timeout, TimeUnit.HOURS);
        }
        leaseOrderMiningMapper.insertBatch(orderMiningList);
        macMessageMap.forEach(this::sendMessage);

    }

    public String combinationKey(String pool,String walletAddress,String coin,String algo){
        return pool +":"+ walletAddress +":"+ coin +":"+ algo;
    }

    /**
     * 发送矿机配置信息给矿机客户端
     * @param mac 矿机mac
     * @param message 消息内容
     */
    public void sendMessage(String mac,ClientMessage<MiningConfigDto> message){
        ChannelManager.sendToClient(mac,message);
    }




    @Override
    public PageResult<PurchasedMachineListDto> getPurchasedItems(PageVo pageVo) {
        String userEmail = SecurityUtils.getUsername();
        PageHelper.startPage(pageVo.getPageNum(), pageVo.getPageSize());

        List<PurchasedMachineListDto> purchasedItems = leaseOrderMiningMapper.getPurchasedItems(userEmail);
        //获取订单信息
        PageInfo<PurchasedMachineListDto> pageInfo = new PageInfo<>(purchasedItems);
        PageResult<PurchasedMachineListDto> success = PageResult.success(purchasedItems);
        success.setTotal(pageInfo.getTotal());
        success.setTotalPage(pageInfo.getPages());
        PageHelper.clearPage();
        return success;
    }

    @Override
    @DSTransactional
    public Result<MiningConfigInfoDto> getPurchasedInfo(BaseVo baseVo) {
        //找到miner 和钱包 ,pool 矿池名
        MiningConfigInfoDto info = leaseOrderMiningMapper.getPurchasedInfo(baseVo.getId());
        List<PurchasedMachineDto> list = leaseOrderMiningMapper.getPurchasedMachineInfo(info);


        //根据钱包 + 币种 + 矿工号 查询 pool 库 pool.kryptex 表 中对应 算力
        Map<String, MiningHashrateInfoDto> recentlyHashRateMap = leaseOrderMiningMapper
                .getRecentlyHashrate(info.getPool(), info.getWalletAddress(), info.getCoin(), list);
        for (PurchasedMachineDto purchasedMachineDto : list) {
            MiningHashrateInfoDto miningHashrateInfoDto = recentlyHashRateMap.get(purchasedMachineDto.getWorkerId());
            if (miningHashrateInfoDto != null){
                purchasedMachineDto.setRecordTime(miningHashrateInfoDto.getRecordTime());
                purchasedMachineDto.setPower(miningHashrateInfoDto.getPower());
            }
        }

        info.setPurchasedMachines(list);
        return Result.success(info);
    }
}
