package com.jwipc.tender.logic.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jwipc.aggregation.ModelAggregationService;
import com.jwipc.constant.FlagConst;
import com.jwipc.exception.DaoException;
import com.jwipc.inquiry.client.PriceInfoClient;
import com.jwipc.inquiry.dto.PriceInfoClientDTO;
import com.jwipc.page.PageUtil;
import com.jwipc.page.PageView;
import com.jwipc.prop.PriceHandle;
import com.jwipc.prop.SysProp;
import com.jwipc.security.LoginUtil;
import com.jwipc.supplier.clent.dto.SupplierAvlBaseDTO;
import com.jwipc.supplier.clent.dto.SupplierDTO;
import com.jwipc.supplier.client.SupplierClient;
import com.jwipc.sys.client.*;
import com.jwipc.sys.client.dto.*;
import com.jwipc.tender.constant.*;
import com.jwipc.tender.convert.*;
import com.jwipc.tender.dto.*;
import com.jwipc.tender.entity.*;
import com.jwipc.tender.logic.BidLogic;
import com.jwipc.tender.model.po.BidDetailPO;
import com.jwipc.tender.model.po.TenderBidListPO;
import com.jwipc.tender.model.query.BidQuery;
import com.jwipc.tender.service.*;
import com.jwipc.util.DateUtils;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.jwipc.tender.constant.QuoteLogType.BARGAIN;
import static com.jwipc.tender.constant.QuoteLogType.QUOTE;


/**
 * <p> </p>
 *
 * @author BOGON
 * @version 1.0
 * @since JDK 1.8
 */
@Service
@RequiredArgsConstructor
public class BidLogicImpl implements BidLogic {
    private static final Logger log = LoggerFactory.getLogger(BidLogicImpl.class);
    private final ITenderBidService tenderBidService;
    private final TenderBidConvert tenderBidConvert;
    private final ITenderDetailService tenderDetailService;
    private final ITenderDetailExtService tenderDetailExtService;
    private final ITenderBidDetailService tenderBidDetailService;
    private final ITenderBidDetailExtService tenderBidDetailExtService;
    private final TenderBidDetailConvert tenderBidDetailConvert;
    private final TenderBidDetailExtConvert tenderBidDetailExtConvert;
    private final ITenderSettingSecurityDepositService tenderSettingSecurityDepositService;
    private final ITenderSecurityDepositService tenderSecurityDepositService;
    private final TenderSettingSecurityConvert tenderSettingSecurityConvert;
    private final FileClient fileClient;
    private final ITenderAttachmentService tenderAttachmentService;
    private final TenderAttachmentConvert tenderAttachmentConvert;
    private final ITenderService tenderService;
    private final ITenderRoundService tenderRoundService;
    private final ModelAggregationService modelAggregationService;
    private final ITenderBidQuoteLogService tenderBidQuoteLogService;
    private final ITenderSettingCompetitiveService tenderSettingCompetitiveService;
    private final TenderDetailConvert tenderDetailConvert;
    private final TenderDetailExtConvert tenderDetailExtConvert;
    private final ExchangeRateClient exchangeRateClient;
    private final TaxClient taxClient;
    private final PriceHandle priceHandle;
    private final SupplierClient supplierClient;
    private final StringRedisTemplate redisTemplate;
    private final EmailSendClient emailSendClient;
    private final UserClient userClient;
    private final SysProp sysProp;
    private final PriceInfoClient priceInfoClient;

    static String BID_LOCK = "BID_DETAIL_LOCK";


    /**
     * 获取列表
     *
     * @param query 查询条件
     * @return 列表
     */
    @Override
    public PageView getConfirmJoinList(BidQuery query) {
        query.setSupplierId(LoginUtil.getSupplierId());
        query.setStatus(String.valueOf(BidStatus.TO_BE_CONFIRMED.getStatus()));
        Page<TenderBidListPO> page = PageUtil.toPage(query);
        List<TenderBidListPO> bidListPOS = tenderBidService.findConfirmJoinList(page, query);
        List<BidConfirmJoinListDTO> bidConfirmJoinListDTOS = tenderBidConvert.pos2Dtos(bidListPOS);
        return PageUtil.getPV(bidConfirmJoinListDTOS, page.getTotal());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitBidStatus(Long id, Integer status, String remark) {
        final TenderBid tenderBid = tenderBidService.getById(id);
        if (Objects.isNull(tenderBid)) {
            throw new DaoException("投标单不存在");
        }
        if (Objects.nonNull(tenderBid.getAgreeFlag())) {
            throw new DaoException("投标单已处理");
        }
        tenderBid.setAgreeFlag(status);
        tenderBid.setDisagreeRemark(remark);
        //如果同意需要生成投标明细
        if (status.equals(FlagConst.Y.getCode())) {
            //如果是有保证金，需要变成缴纳中,如果没有需要改成报价中
            final TenderSettingSecurityDeposit securityDeposit = tenderSettingSecurityDepositService.getById(
                    tenderBid.getTenderId());
            if (Objects.nonNull(securityDeposit)) {
                tenderBid.setStatus(BidStatus.DEPOSIT_PENDING.getStatus());
            } else {
                tenderBid.setStatus(BidStatus.BIDDING.getStatus());
            }
            generateBiddingDetails(tenderBid);
        } else {
            tenderBid.setStatus(BidStatus.NOT_PARTICIPATING.getStatus());
        }
        tenderBidService.updateById(tenderBid);


    }

    private void generateBiddingDetails(TenderBid tenderBid) {
        //增加redis锁如果这个供应商的数据正在处理就直接返回
        final String key = BID_LOCK + tenderBid.getId();
        if (!redisTemplate.opsForValue().setIfAbsent(key, "1", 30, TimeUnit.SECONDS)) {
            log.info("投标单正在处理中，请稍后");
            return;
        }

        //取招标单的明细
        final List<TenderDetail> tenderDetails = tenderDetailService.list(
                new LambdaQueryWrapper<TenderDetail>().eq(TenderDetail::getTenderId, tenderBid.getTenderId()));
        final Set<Long> detailIds = tenderDetails.stream().map(TenderDetail::getId).collect(Collectors.toSet());
        final List<TenderDetailExt> tenderDetailExts = tenderDetailExtService.listByIds(detailIds);
        final Map<Long, TenderDetailExt> idMapCache = tenderDetailExts.stream()
                .collect(Collectors.toMap(TenderDetailExt::getId, tenderDetailExt -> tenderDetailExt));
        final Long tenderBidId = tenderBid.getId();
        final Long tenderId = tenderBid.getTenderId();
        List<TenderBidDetail> tenderBidDetails = Lists.newArrayList();
        List<TenderBidDetailExt> tenderBidDetailExts = Lists.newArrayList();
        final SupplierDTO supplierDTO = supplierClient.getSupplierById(tenderBid.getSupplierId());
        for (TenderDetail tenderDetail : tenderDetails) {
            TenderBidDetail tenderBidDetail = tenderBidDetailConvert.tenderDetail2Entity(tenderDetail);
            tenderBidDetail.setBidId(tenderBidId);
            tenderBidDetail.setTenderDetailId(tenderBidDetail.getId());
            tenderBidDetail.setTenderId(tenderId);
            tenderBidDetail.setSupplierId(tenderBid.getSupplierId());
            final long id = IdWorker.getId();
            tenderBidDetail.setId(id);
            tenderBidDetail.setTaxRateCode(tenderBid.getTaxCode());
            tenderBidDetail.setCurrency(tenderBid.getCurrency());
            tenderBidDetail.setExchangeRate(tenderBid.getExchangeRate());

            tenderBidDetails.add(tenderBidDetail);
            final TenderDetailExt tenderDetailExt = idMapCache.get(tenderDetail.getId());
            TenderBidDetailExt tenderBidDetailExt = tenderBidDetailExtConvert.tenderDetailExt2Entity(tenderDetailExt);
            tenderBidDetailExt.setId(id);
            //赋值Avl信息
            if (StringUtils.isNotEmpty(tenderBidDetail.getMaterialCode())) {
                SupplierAvlBaseDTO supplierAvlBaseDTO = supplierClient.findSupplierAvlItem(
                        supplierDTO.getSupplierCode(), tenderBidDetail.getMaterialCode());
                if (ObjectUtil.isNotNull(supplierAvlBaseDTO)) {
                    tenderBidDetailExt.setMpn(supplierAvlBaseDTO.getMpn());
                    tenderBidDetailExt.setEccn(supplierAvlBaseDTO.getEccn());
                    tenderBidDetailExt.setManufacturer(supplierAvlBaseDTO.getProductName());
                    tenderBidDetailExt.setManufacturerEn(supplierAvlBaseDTO.getProductNameEn());
                    tenderBidDetailExt.setCountryName(supplierAvlBaseDTO.getCountryCode());
                    if (StrUtil.isNotEmpty(supplierAvlBaseDTO.getCif())) {
                        tenderBidDetailExt.setK21(new BigDecimal(supplierAvlBaseDTO.getCif()));
                    }
                    if (StrUtil.isNotEmpty(supplierAvlBaseDTO.getMpq())) {
                        tenderBidDetail.setMpq(Long.valueOf(supplierAvlBaseDTO.getMpq()));
                    }
                    if (StrUtil.isNotEmpty(supplierAvlBaseDTO.getMoq())) {
                        tenderBidDetail.setMoq(Long.valueOf(supplierAvlBaseDTO.getMoq()));
                    }


                }
            }
            tenderBidDetailExts.add(tenderBidDetailExt);
        }
        if (CollUtil.isNotEmpty(tenderBidDetails)) {
            tenderBidDetailService.saveBatch(tenderBidDetails);
        }
        if (CollUtil.isNotEmpty(tenderBidDetailExts)) {
            tenderBidDetailExtService.saveBatch(tenderBidDetailExts);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitQuotation(SubmitQuotationDTO submitQuotationDTO) {
        final TenderBidBaseDTO bid = submitQuotationDTO.getBid();
        final Long bidId = bid.getId();
        final TenderBid oldTtenderBid = tenderBidService.getById(bidId);
        //需要判断是否在时间范围内，如果是就提示
        //可以报价的状态是 发布-报价，已开标-议价
        final Long tenderId = oldTtenderBid.getTenderId();
        final Tender tender = tenderService.getById(tenderId);
        final TenderRound tenderRound = tenderRoundService.getById(tender.getRoundId());
        if (Objects.isNull(tender)) {
            throw new DaoException("招标单不存在");
        } else if (tender.getStatus() != TenderStatus.RELEASE.getStatus() &&
                tender.getStatus() != TenderStatus.OPENED.getStatus()) {
            throw new DaoException("招标单状态符合投标条件");
        } else if (LocalDateTime.now().isAfter(tenderRound.getEndDay())) {
            throw new DaoException("投标时间已过，无法再投标!");
        } else if (LocalDateTime.now().isBefore(tenderRound.getStartDay())) {
            throw new DaoException("投标时间未到，无法投标!");
        }
        TenderBid tenderBid = tenderBidConvert.dto2Entity(bid);
        tenderBid.setStatus(BidStatus.BIDDED.getStatus());
        final Pair<BigDecimal, BigDecimal> cnyPrice = getCnyPrice(tenderBid.getCurrency(), tenderBid.getTotalAmount());
        tenderBid.setFinalAmount(tenderBid.getFinalAmount());//含税报价
        tenderBid.setTotalAmount(tenderBid.getTotalAmount());//不含税报价
        /**
         * 报价的时候这个总金额是不含税报价
         */
        tenderBid.setFinalCnyAmount(cnyPrice.getValue().setScale(2, RoundingMode.DOWN));
        tenderBid.setExchangeRate(cnyPrice.getKey());
        tenderBid.setTenderTime(LocalDateTime.now());
        tenderBidService.updateById(tenderBid);
        //投标附件如果是修改需要把附件都删除重新插入一变
        tenderAttachmentService.remove(
                new LambdaQueryWrapper<TenderAttachment>().eq(TenderAttachment::getMasterId, tenderBid.getId())
                        .in(TenderAttachment::getType, TenderAttachmentType.getBidFileTypes()));

        final List<TenderAttachmentDTO> bidFiles = submitQuotationDTO.getBidFiles();
        final List<TenderAttachment> tenderAttachments = tenderAttachmentConvert.dtos2Entities(bidFiles);
        for (TenderAttachment tenderAttachment : tenderAttachments) {
            tenderAttachment.setMasterId(bid.getId());
        }
        tenderAttachmentService.saveBatch(tenderAttachments);

        //保存行明细
        final List<TenderBidDetailAllDTO> bidDetail = submitQuotationDTO.getBidDetail();
        List<TenderBidDetail> bidDetails = tenderBidDetailConvert.dtos2Entities(bidDetail);
        for (TenderBidDetail detail : bidDetails) {
            detail.setMaterialDesc(null);
            detail.setMaterialName(null);
        }
        //判断如果报价行没有就是未报价
        bidDetails.stream().filter(a -> Objects.nonNull(a.getTaxPrice())).forEach(a -> {
            a.setOfferStatus(FlagConst.Y.getCode());
        });
        List<TenderBidDetailExt> bidDetailExts = tenderBidDetailConvert.dtos2ExtEntities(bidDetail);
        List<TenderBidQuoteLog> insertLists = Lists.newArrayList();
        final LocalDateTime now = LocalDateTime.now();
        if (CollUtil.isNotEmpty(bidDetails)) {
            final Set<String> taxCodes = bidDetails.stream().map(TenderBidDetail::getTaxRateCode)
                    .collect(Collectors.toSet());
            final List<TaxDTO> taxList = taxClient.getTaxList(taxCodes);
            final Map<String, BigDecimal> taxCache = taxList.stream()
                    .collect(Collectors.toMap(TaxDTO::getTaxCode, TaxDTO::getTaxValue));
            for (TenderBidDetail detail : bidDetails) {
                if (Objects.isNull(detail.getTaxPrice())) {
                    continue;
                }
                detail.setFinalPrice(detail.getTaxPrice());
                final BigDecimal taxValue = taxCache.get(detail.getTaxRateCode());
                //计算未税价格
                final BigDecimal noTaxPrice = detail.getTaxPrice()
                        .divide(taxValue.add(BigDecimal.ONE), priceHandle.getPrecision(),
                                Objects.equals(priceHandle.getCutType(),
                                        FlagConst.N.getCode()) ? RoundingMode.HALF_UP : RoundingMode.DOWN);
                insertLists.add(
                        createQuoteLog(detail.getBidId(), detail.getTenderId(), detail.getId(), BigDecimal.ZERO,
                                detail.getFinalPrice(),
                                noTaxPrice, now));
                detail.setCurrentPrice(noTaxPrice);
                detail.setFinalNoTaxPrice(noTaxPrice);
                final Pair<BigDecimal, BigDecimal> price = getCnyPrice(detail.getCurrency(),
                        detail.getFinalNoTaxPrice());
                detail.setFinalCnyAmount(price.getValue().setScale(priceHandle.getPrecision(),
                        Objects.equals(priceHandle.getCutType(),
                                FlagConst.N.getCode()) ? RoundingMode.HALF_UP : RoundingMode.DOWN));//不含税人民币单价
                final Pair<BigDecimal, BigDecimal> taxPrice = getCnyPrice(detail.getCurrency(),
                        detail.getTaxPrice());
                detail.setFinalCnyTaxAmount(taxPrice.getValue().setScale(priceHandle.getPrecision(),
                        Objects.equals(priceHandle.getCutType(),
                                FlagConst.N.getCode()) ? RoundingMode.HALF_UP : RoundingMode.DOWN));//含税人民币单价
                detail.setExchangeRate(price.getKey());
            }
            tenderBidDetailService.updateBatchById(bidDetails);
        }
        if (CollUtil.isNotEmpty(bidDetailExts)) {
            tenderBidDetailExtService.updateBatchById(bidDetailExts);
        }
        //如果是分项报价需要重新统计 finalCnyAmount
        if (tender.getTypeId().equals(1L)) {
            final BigDecimal sumCnyTotal = getSumCnyTotal(bidId);
            tenderBid.setFinalCnyAmount(sumCnyTotal.setScale(2, RoundingMode.DOWN));
            tenderBidService.updateById(tenderBid);
        }
        // 竞价的验证
        quotationValidation(tenderBidService.getById(bidId), tender);
        if (CollUtil.isNotEmpty(insertLists)) {
            for (TenderBidQuoteLog insertList : insertLists) {
                insertList.setTaxTotalPrice(tenderBid.getFinalAmount());
            }
            tenderBidQuoteLogService.saveBatch(insertLists);
        } else {
            tenderBidQuoteLogService.save(
                    createQuoteLog(tenderBid.getId(), tenderId, 0L, tenderBid.getFinalAmount(),
                            BigDecimal.ZERO,
                            BigDecimal.ZERO, now));
        }
        //发送已投标邮件 2024-10-17 邓京术
        sendSubmitQuotationEmail(oldTtenderBid,tender,"JW_POST_111");
        //如果所有供应商都投标完成，则发送全部投标完成邮件通知采购 2024-10-17 邓京术
        if (null != oldTtenderBid.getStatus() && oldTtenderBid.getStatus() == BidStatus.BIDDING.getStatus()) {
            //查询所有的其他投标供应商，是否已经投标完成
            final List<TenderBid> tenderBidList = tenderBidService.list(
                    new LambdaQueryWrapper<TenderBid>().eq(TenderBid::getTenderId, oldTtenderBid.getTenderId()).ne(TenderBid::getSupplierId, oldTtenderBid.getSupplierId()));
            if (tenderBidList.size() == 0 || tenderBidList.stream().allMatch(item -> item.getStatus() == BidStatus.BIDDED.getStatus())) {
                //发送邮件提醒，全部参与该标的供应商已投标完成
                sendSubmitQuotationEmail(oldTtenderBid, tender, "JW_POST_112");
            }
        }
    }

    private void sendSubmitQuotationEmail(TenderBid tenderBid, Tender tender,String templeteCode) {
        final SupplierDTO supplierDTO = supplierClient.getSupplierById(tenderBid.getSupplierId());
        Map<String, Object> params = Maps.newHashMap();
        params.put("inquiryTitle", tender.getTitle());
        params.put("supplierName", supplierDTO.getSupplierName());
        params.put("url", sysProp.getWebsiteUrl());
        //获取采购
        UserDTO user = userClient.getByUserId(tender.getCreateId());
        final EmailTmpSingleSendDTO emailTxtSingleSendDTO = new EmailTmpSingleSendDTO();
        emailTxtSingleSendDTO.setTemplateCode(templeteCode);
        emailTxtSingleSendDTO.setParams(params);
        emailTxtSingleSendDTO.setReceiver(user.getEmail());
        emailTxtSingleSendDTO.setSender(LoginUtil.getUserId());
        emailSendClient.async(emailTxtSingleSendDTO);
    }

    @Override
    public PageView getSecurityDepositList(BidQuery query) {
        Page<TenderBidListPO> page = PageUtil.toPage(query);
        query.setSupplierId(LoginUtil.getSupplierId());
        query.setStatus(String.valueOf(BidStatus.DEPOSIT_PENDING.getStatus()));
        List<TenderBidListPO> listPOS = tenderBidService.findSecurityDepositList(page, query);
        List<BidSecurityDepositListDTO> securityDepositListDTOS = tenderBidConvert.pos2Dtos1(listPOS);
        modelAggregationService.processRelationAggregation(securityDepositListDTOS);
        return PageUtil.getPV(securityDepositListDTOS, page.getTotal());
    }

    @Override
    public BidDetailDTO getDetail(Long id) {
        final TenderBid tenderBid = tenderBidService.getById(id);
        final Tender tender = tenderService.getById(tenderBid.getTenderId());
        //处理保证金信息
        final TenderSecurityDeposit depositServiceOne = tenderSecurityDepositService.getOne(
                new LambdaQueryWrapper<TenderSecurityDeposit>().eq(TenderSecurityDeposit::getBidId, id));
        final TenderSettingSecurityDeposit securityDeposit = tenderSettingSecurityDepositService.getById(
                tenderBid.getTenderId());
        TenderSecurityDepositDTO tenderSecurityDepositDTO = tenderSettingSecurityConvert.setting2Dto(securityDeposit);
        if (Objects.nonNull(depositServiceOne)) {
            tenderSecurityDepositDTO = tenderSettingSecurityConvert.merge(depositServiceOne, tenderSecurityDepositDTO);
            if (Objects.nonNull(tenderSecurityDepositDTO.getFileId())) {
                final FileResposeCDTO fileInfo = fileClient.getFileInfo(tenderSecurityDepositDTO.getFileId());
                if (Objects.nonNull(fileInfo)) {
                    tenderSecurityDepositDTO.setFileName(fileInfo.getFileName());
                }
            }
        }

        BidDetailDTO bidDetailDTO = new BidDetailDTO();
        bidDetailDTO.setSecurityDeposit(tenderSecurityDepositDTO);
        //处理投标明细
        final List<TenderBidDetail> list = tenderBidDetailService.list(
                new LambdaQueryWrapper<TenderBidDetail>().eq(TenderBidDetail::getBidId, id));
        final Set<Long> detailIds = list.stream().map(TenderBidDetail::getId).collect(Collectors.toSet());
        final List<TenderBidDetailExt> detailExts = Lists.newArrayList();
        if (CollUtil.isNotEmpty(detailIds)) {
            detailExts.addAll(tenderBidDetailExtService.listByIds(detailIds));
        }

        final Map<Long, TenderBidDetailExt> extCache = detailExts.stream()
                .collect(Collectors.toMap(TenderBidDetailExt::getId, a -> a));
        List<TenderBidDetailAllDTO> tenderBidDetailAllDTOS = tenderBidDetailConvert.entities2AllDtos(list);
        for (TenderBidDetailAllDTO tenderBidDetailAllDTO : tenderBidDetailAllDTOS) {
            //需要判断供应商描述,如果是短描述需要
            if (tender.getSupplierMaterialFlag().equals(SupplierMaterialFlag.SHORT_NAME.getCode())) {
                for (TenderBidDetailAllDTO tenderDetailDTO : tenderBidDetailAllDTOS) {
                    tenderDetailDTO.setMaterialName(tenderDetailDTO.getMaterialDesc());
                }
            }
            final TenderBidDetailExt tenderBidDetailExt = extCache.get(tenderBidDetailAllDTO.getId());
            if (Objects.nonNull(tenderBidDetailExt)) {
                tenderBidDetailConvert.mergeToAllDtos(tenderBidDetailAllDTO, tenderBidDetailExt);
            }
        }
        //如果投标明细不存在需要去取招标单明细数据
        if (CollUtil.isEmpty(tenderBidDetailAllDTOS)) {
            //投标明细
            List<TenderDetailAllDTO> tenderDetailDTOS = tenderDetailService.list(
                            new LambdaQueryWrapper<TenderDetail>().eq(TenderDetail::getTenderId,
                                    tenderBid.getTenderId()))
                    .stream().map(tenderDetailConvert::entity2Dto).collect(Collectors.toList());
            //需要判断供应商描述,如果是短描述需要
            if (tender.getSupplierMaterialFlag().equals(SupplierMaterialFlag.SHORT_NAME.getCode())) {
                for (TenderDetailAllDTO tenderDetailDTO : tenderDetailDTOS) {
                    tenderDetailDTO.setMaterialName(tenderDetailDTO.getMaterialDesc());
                }
            }
            final List<Long> tenderDetailIds = tenderDetailDTOS.stream().map(tenderDetailDTO -> tenderDetailDTO.getId())
                    .collect(Collectors.toList());
            if (CollUtil.isNotEmpty(tenderDetailIds)) {
                final List<TenderDetailExt> tenderDetailExts = tenderDetailExtService.listByIds(tenderDetailIds);
                final Map<Long, TenderDetailExt> collect = tenderDetailExts.stream()
                        .collect(Collectors.toMap(TenderDetailExt::getId, tenderDetailExt -> tenderDetailExt));
                for (TenderDetailAllDTO tenderDetailDTO : tenderDetailDTOS) {
                    final TenderDetailExt tenderDetailExt = collect.get(tenderDetailDTO.getId());
                    if (Objects.nonNull(tenderDetailExt)) {
                        tenderDetailExtConvert.entity2Dto(tenderDetailExt, tenderDetailDTO);
                    }
                }
            }
            bidDetailDTO.setBidDetail(tenderBidDetailConvert.tenderDetail2AllDtos(tenderDetailDTOS));
        } else {
            bidDetailDTO.setBidDetail(tenderBidDetailAllDTOS);
        }

        //处理投标单信息
        TenderBidBaseDTO tenderBidBaseDTO = tenderBidConvert.entity2Dto(tenderBid);
        //2025-03-12新增逻辑总价和排名时时计算逻辑,这里需要考虑此时的投标状态是否是定标前的一个状态
        Integer status = tender.getStatus();
        if(status.intValue() != 9 && status.intValue() != 10 && status.intValue() != 5 ){
            List<TenderBid> tenderBids = calculateSumPriceRanking(tenderBid.getTenderId(),tender);
            if(CollUtil.isNotEmpty(tenderBids)){
                for (TenderBid  bid : tenderBids){
                    if(bid.getSupplierId().equals(tenderBidBaseDTO.getSupplierId())){
                        tenderBidBaseDTO.setRanking(bid.getRanking());
                        break;
                    }
                }

            }
        }

        //需要处理是否显示排名
        final TenderSettingCompetitive byId = tenderSettingCompetitiveService.getById(tenderBid.getTenderId());
        if (byId.getRankFlag().equals(FlagConst.N.getCode())) {
            tenderBidBaseDTO.setRanking(null);
        }
        bidDetailDTO.setBid(tenderBidBaseDTO);
        //处理投标附件信息
        final List<TenderAttachment> tenderAttachments = tenderAttachmentService.list(
                new LambdaQueryWrapper<TenderAttachment>().eq(TenderAttachment::getMasterId, id)
                        .in(TenderAttachment::getType, TenderAttachmentType.getBidFileTypes()));
        final List<TenderAttachmentDTO> tenderAttachmentDTO = tenderAttachmentConvert.entities2Dtos(tenderAttachments);
        modelAggregationService.processRelationAggregation(tenderAttachmentDTO);
        bidDetailDTO.setBidFiles(tenderAttachmentDTO);
        return bidDetailDTO;
    }

    /**
     * 时时计算招标物料报价总价格及排名
     * @param id 招标单id
     * @return
     */
    public List<TenderBid> calculateSumPriceRanking(Long id,Tender tender){
        final List<TenderBid> tenderBids = tenderBidService.list(
                new LambdaQueryWrapper<>(TenderBid.class).eq(TenderBid::getTenderId, id).eq(TenderBid::getAgreeFlag,1).ne(TenderBid::getStatus,3)//不等于3
                        .orderByAsc(TenderBid::getRanking));
        //2025-03-03新增修改逻辑
        //思路：1.先把供应商需要进行报价的物料价格数据全部找出来
        //2.通过 taxPrice 含税单价来判断是否报价了，能否取到历史配价格的
        //3.动态的查询未报价的在系统里最新有效期的价格给它附上值
        //4.再进行一个排名的排序返回给前端
        //需要把排名为空的放置最后
        List<Long> ids = tenderBids.stream()
                .map(TenderBid::getId) // 将每个TenderBid对象映射到其id
                .collect(Collectors.toList()); // 收集到List中
        LambdaQueryWrapper<TenderBidDetail> eq = new LambdaQueryWrapper<TenderBidDetail>().eq(TenderBidDetail::getTenderId, id);
        if(CollectionUtils.isNotEmpty(ids)){
            eq.in(TenderBidDetail::getBidId,ids);
        }
        eq.orderByAsc(TenderBidDetail::getRowNo);
        final List<TenderBidDetail> list = tenderBidDetailService.list(eq);
        //把报价为空的供应商和物料全部查询出来，然后去价格信息表中进行
        //键是供应商id 值是为空报价的物料报价明细
        Map<Long, List<TenderBidDetail>> result = list.stream()
                .collect(Collectors.groupingBy(TenderBidDetail::getSupplierId))
                .entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        e -> e.getValue().stream()
                                .filter(detail -> detail.getTaxPrice() == null || detail.getTaxPrice().compareTo(BigDecimal.ZERO) == 0)
                                .collect(Collectors.toList())
                ));
        //合并所有未报价的物料用于查询价格
        List<String> materialCodes = list.stream()
                .filter(detail -> detail.getTaxPrice() != null && detail.getTaxPrice().compareTo(BigDecimal.ZERO) > 0)
                .map(TenderBidDetail::getMaterialCode)
                .collect(Collectors.toSet()) // 使用Set去重
                .stream() // 再转换回List
                .collect(Collectors.toList());
        if(CollUtil.isNotEmpty(materialCodes)){
            //到价格表里查找有效且时间是最近的通过价格有效日期开始和结束日期
            List<PriceInfoClientDTO> effectiveMinPrice = priceInfoClient.getEffectiveMinPrice(materialCodes, new Date());
            Map<String, BigDecimal> priceMap = new HashMap<>();
            for (PriceInfoClientDTO dto : effectiveMinPrice) {
                priceMap.put(dto.getMaterialCode(), dto.getConversionPrice());
            }
            //供应商未报价
            Map<Long, BigDecimal> countPriceMap = new HashMap<>();
            for (Map.Entry<Long, List<TenderBidDetail>> entry : result.entrySet()) {
                Long supplierId = entry.getKey(); // 获取supplierId
                List<TenderBidDetail> details = entry.getValue(); // 获取details列表
                if(CollectionUtils.isEmpty(details)){
                    continue;
                }
                BigDecimal totalDetailAmount = BigDecimal.ZERO;
                for (TenderBidDetail detail : details) {
                    String materialCode = detail.getMaterialCode(); // 获取MaterialCode
                    if(priceMap.containsKey(materialCode)){
                        BigDecimal price = priceMap.getOrDefault(materialCode, BigDecimal.ZERO) == null?BigDecimal.ZERO:priceMap.getOrDefault(materialCode, BigDecimal.ZERO);
                        //设置价格，计算公式 转换后价格 * 投标数量 count
                        BigDecimal detailAmount = price.multiply(detail.getCount() == null ? BigDecimal.ZERO:detail.getCount()).setScale(2, RoundingMode.DOWN);
                        totalDetailAmount = totalDetailAmount.add(detailAmount);
                    }
                }
                countPriceMap.put(supplierId, totalDetailAmount);//供应商对应的 未报价物料的未税总价格
            }

            //将总价设置到TenderBid数据信息里
            tenderBids.forEach(tenderBid -> {
                Long supplierId = tenderBid.getSupplierId();
                BigDecimal bigDecimal = countPriceMap.get(supplierId);
                if(bigDecimal != null){
                    tenderBid.setFinalCnyAmount(tenderBid.getFinalCnyAmount() == null ?bigDecimal : tenderBid.getFinalCnyAmount().add(bigDecimal));
                }
            });
            // 使用Comparator对tenderBids进行排序
            Collections.sort(tenderBids, Comparator.comparing(TenderBid::getFinalCnyAmount,Comparator.nullsLast(Comparator.naturalOrder())));
            // 设置排名 setRanking
            int rank = 1;
            //选择“呆滞销售招标”时，系统按“单价越高排名越前”的规则进行报价排名；
            if ( "1".equals(tender.getTendType())){
                Collections.sort(tenderBids, Comparator.comparing(TenderBid::getFinalCnyAmount,Comparator.nullsLast(Comparator.reverseOrder())));
            }
            BigDecimal previousAmount = BigDecimal.valueOf(Double.MIN_VALUE);
            for (TenderBid bid : tenderBids) {
                BigDecimal finalCnyAmount = bid.getFinalCnyAmount();
                if(finalCnyAmount == null ){
                    finalCnyAmount = BigDecimal.ZERO;
                }
                if (finalCnyAmount.compareTo(previousAmount) != 0) {
                    previousAmount = bid.getFinalCnyAmount();
                    bid.setRanking(rank);
                }
                rank++;
            }
        }
        return tenderBids;
    }

    @Override
    public void submitDeposit(Long id, Long fileId) {
        final TenderBid bid = tenderBidService.getById(id);
        TenderSecurityDeposit securityDeposit = tenderSecurityDepositService.getOne(
                new LambdaQueryWrapper<TenderSecurityDeposit>().eq(TenderSecurityDeposit::getBidId, id), false);
        if (Objects.nonNull(securityDeposit)) {
            securityDeposit.setStatus(DepositStatus.APPROVING.getValue());
            securityDeposit.setUpdateId(LoginUtil.getUserId());
            securityDeposit.setUpdateTime(LocalDateTime.now());
        } else {
            securityDeposit = new TenderSecurityDeposit();
            securityDeposit.setBidId(id);
            securityDeposit.setTenderId(bid.getTenderId());
            securityDeposit.setCreateId(LoginUtil.getUserId());
            securityDeposit.setCreateTime(LocalDateTime.now());
        }
        securityDeposit.setFileId(fileId);
        tenderSecurityDepositService.saveOrUpdate(securityDeposit);
    }

    @Override
    public PageView getToBeQuotedList(BidQuery query) {
        Page<TenderBidListPO> page = PageUtil.toPage(query);
        query.setSupplierId(LoginUtil.getSupplierId());
        List<TenderBidListPO> tenderBidListPOS = tenderBidService.findToBeQuotedList(page, query);
        List<ToBeQuotedListDTO> listDTOS = tenderBidConvert.pos2Dtos2(tenderBidListPOS);
        modelAggregationService.processRelationAggregation(listDTOS);
        return PageUtil.getPV(listDTOS, page.getTotal());
    }

    /**
     * 议价的时候供应商提供的价格是含税价格
     *
     * @param submitQuotationDTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitBargaining(SubmitBargainingDTO submitQuotationDTO) {
        final List<TenderBidDetail> tenderBidDetails = tenderBidDetailConvert.dtos2Entities2(
                submitQuotationDTO.getItems());
        for (TenderBidDetail detail : tenderBidDetails) {
            detail.setMaterialDesc(null);
            detail.setMaterialName(null);
        }
        final List<TenderBidDetailExt> tenderBidDetailExts = tenderBidDetailConvert.dtos2Entities3(
                submitQuotationDTO.getItems());
        List<Long> bidIds = tenderBidDetails.stream().map(TenderBidDetail::getBidId).collect(Collectors.toSet())
                .stream().toList();
        Set<Long> detailIds = tenderBidDetails.stream().map(TenderBidDetail::getId).collect(Collectors.toSet());
        if (bidIds.size() != 1) {
            throw new DaoException("只能提交一个投标单的报价!");
        }

        final List<TenderAttachmentDTO> bidFiles = submitQuotationDTO.getBidFiles();
        final List<TenderAttachment> tenderAttachments = tenderAttachmentConvert.dtos2Entities(bidFiles);
        for (TenderAttachment tenderAttachment : tenderAttachments) {
            tenderAttachment.setMasterId(bidIds.get(0));
        }
        tenderAttachmentService.saveBatch(tenderAttachments);
        //需要获取TenderBidDetail
        final List<TenderBidDetail> bidDetails = tenderBidDetailService.listByIds(detailIds);
        final Map<Long, BigDecimal> exchangeRateCache = bidDetails.stream()
                .collect(Collectors.toMap(TenderBidDetail::getId, TenderBidDetail::getExchangeRate));
        final Long bidId = tenderBidDetails.get(0).getBidId();
        final Set<String> taxCodes = bidDetails.stream().map(TenderBidDetail::getTaxRateCode)
                .collect(Collectors.toSet());
        final List<TaxDTO> taxList = taxClient.getTaxList(taxCodes);
        final Map<String, BigDecimal> taxCache = taxList.stream()
                .collect(Collectors.toMap(TaxDTO::getTaxCode, TaxDTO::getTaxValue));
        List<TenderBidQuoteLog> bidQuoteLogs = Lists.newArrayList();
        final LocalDateTime now = LocalDateTime.now();
        tenderBidDetails.forEach(a -> {
            final BigDecimal bargainingSupplier = a.getBargainingSupplier();
            if (Objects.nonNull(bargainingSupplier)) {
                a.setFinalPrice(bargainingSupplier);
                final BigDecimal taxValue = taxCache.get(a.getTaxRateCode());
                //计算未税价格
                final BigDecimal noTaxPrice = a.getFinalPrice()
                        .divide(taxValue.add(BigDecimal.ONE), priceHandle.getPrecision(),
                                Objects.equals(priceHandle.getCutType(),
                                        FlagConst.N.getCode()) ? RoundingMode.HALF_UP : RoundingMode.DOWN);
                bidQuoteLogs.add(
                        createBargainLog(a.getBidId(), a.getTenderId(), a.getId(), BigDecimal.ZERO, a.getFinalPrice(),
                                noTaxPrice, now));
                a.setFinalCnyAmount(
                        noTaxPrice.multiply(exchangeRateCache.get(a.getId())).setScale(priceHandle.getPrecision(),
                                Objects.equals(priceHandle.getCutType(),
                                        FlagConst.N.getCode()) ? RoundingMode.HALF_UP : RoundingMode.DOWN));//不含税人民币单价
                a.setFinalCnyTaxAmount(bargainingSupplier.multiply(exchangeRateCache.get(a.getId()))
                        .setScale(priceHandle.getPrecision(),
                                Objects.equals(priceHandle.getCutType(),
                                        FlagConst.N.getCode()) ? RoundingMode.HALF_UP : RoundingMode.DOWN));//含税人民币单价
                a.setFinalNoTaxPrice(noTaxPrice);
            }
            a.setOfferStatus(FlagConst.Y.getCode());
            a.setBargainingStatus(BargainingStatus.BARGAIN_SUCCESS.getValue());
        });
        tenderBidService.update(
                new LambdaUpdateWrapper<TenderBid>().set(TenderBid::getStatus, BidStatus.BIDDED.getStatus())
                        .in(TenderBid::getId, bidIds));
        tenderBidDetailService.updateBatchById(tenderBidDetails);
        tenderBidDetailExtService.updateBatchById(tenderBidDetailExts);
        //需要校验议价是否规则，就是所有的价格*数量累加要小于单头的总金额
        final BigDecimal sumPrice = getSumPrice(bidId);//含税报价总金额
        final BigDecimal sumnoTaxPrice = getTaxSumPrice(bidId);//不含税报价总金额
        final BigDecimal sumCnyTotal = getSumCnyTotal(bidId);
        final TenderBid tenderBid = tenderBidService.getById(bidId);
        Tender ten = tenderService.getById(tenderBid.getTenderId());
        //呆滞销售招标，议价时，允许供应商提高价格；
        if ( !"1".equals(ten.getTendType()) &&  Objects.nonNull(tenderBid.getFinalCnyAmount()) &&
                tenderBid.getFinalCnyAmount().compareTo(sumCnyTotal) < 0) {
            log.info("投标总金额:{},议价总金额:{}",tenderBid.getFinalCnyAmount(),sumCnyTotal);
            throw new DaoException("议价金额不能大于投标总金额!");
        }
        tenderBid.setTotalAmount(sumnoTaxPrice);
        tenderBid.setFinalAmount(sumPrice);
        tenderBid.setFinalCnyAmount(sumCnyTotal.setScale(2, RoundingMode.DOWN));
        tenderBidService.saveOrUpdate(tenderBid);
        //需要计算排名
        //是否需要显示排名
        final Long tenderId = bidDetails.get(0).getTenderId();
        //获取全部报价然后判断当前价格是否是最低价格
        //需要进行排名
        final List<TenderBid> tenderBids = tenderBidService.list(
                new LambdaQueryWrapper<TenderBid>().eq(TenderBid::getTenderId, tenderId)
                        .isNotNull(TenderBid::getFinalCnyAmount).orderBy(true, true, TenderBid::getFinalCnyAmount));
        // 使用Comparator对tenderBids进行排序
        Collections.sort(tenderBids, Comparator.comparing(TenderBid::getFinalCnyAmount));
        // 设置排名
        int rank = 1;
        BigDecimal previousAmount = BigDecimal.valueOf(Double.MIN_VALUE);
        for (TenderBid bid : tenderBids) {
            if (bid.getFinalCnyAmount().compareTo(previousAmount) != 0) {
                previousAmount = bid.getFinalCnyAmount();
                bid.setRanking(rank);
            }
            rank++;
        }
        tenderBidService.updateBatchById(tenderBids);
        if (CollUtil.isNotEmpty(bidQuoteLogs)) {
            for (TenderBidQuoteLog bidQuoteLog : bidQuoteLogs) {
                bidQuoteLog.setTaxTotalPrice(tenderBid.getFinalAmount());
            }
            tenderBidQuoteLogService.saveBatch(bidQuoteLogs);
        }
    }

    private BigDecimal getTaxSumPrice(Long bidId) {
        final List<TenderBidDetail> list = tenderBidDetailService.list(
                new LambdaQueryWrapper<TenderBidDetail>().eq(TenderBidDetail::getBidId, bidId));
        AtomicReference<BigDecimal> totalAmount = new AtomicReference<>(BigDecimal.ZERO);
        final List<TaxDTO> all = taxClient.getAll();
        final Map<String, BigDecimal> taxCache = all.stream()
                .collect(Collectors.toMap(a -> a.getTaxCode(), TaxDTO::getTaxValue));
        list.stream().filter(a -> Objects.nonNull(a.getFinalPrice())).forEach(a -> {
            final BigDecimal finalPrice = a.getFinalPrice();
            final BigDecimal count = a.getCount();
            final String taxRateCode = a.getTaxRateCode();
            final BigDecimal bigDecimal = taxCache.get(taxRateCode);
            final BigDecimal noTaxPrice = finalPrice.divide(bigDecimal.add(BigDecimal.ONE), 6, RoundingMode.DOWN);
            final BigDecimal finalAmount = noTaxPrice.multiply(count);
            totalAmount.set(totalAmount.get().add(finalAmount));
        });
        return totalAmount.get();
    }

    @Override
    public PageView getHistoryList(BidQuery query) {
        Page<TenderBidListPO> page = PageUtil.toPage(query);
        query.setSupplierId(LoginUtil.getSupplierId());
        List<String> statusLists = Lists.newArrayList();
        statusLists.add(String.valueOf(BidStatus.NOT_PARTICIPATING.getStatus()));
        statusLists.add(String.valueOf(BidStatus.WON.getStatus()));
        statusLists.add(String.valueOf(BidStatus.BIDDING.getStatus()));
        statusLists.add(String.valueOf(BidStatus.BIDDED.getStatus()));
        statusLists.add(String.valueOf(BidStatus.LOST.getStatus()));
        statusLists.add(String.valueOf(BidStatus.CANCELLED.getStatus()));
        query.setStatus(statusLists.stream().collect(Collectors.joining(StrUtil.COMMA)));
        List<TenderBidListPO> bidListPOS = tenderBidService.getHistoryList(page, query);
        List<BidHistoryListDTO> bidHistoryListDTOS = tenderBidConvert.pos2Dtos3(bidListPOS);
        modelAggregationService.processRelationAggregation(bidHistoryListDTOS);
        return PageUtil.getPV(bidHistoryListDTOS, page.getTotal());
    }

    @Override
    public List<BidDetailDTO> getBidTenderListByPurchaseApplyCode(String purchaseApplyCode, String itemNo) {
        List<BidDetailPO> bidTenderListByPurchaseApplyCode = tenderBidDetailService.getBidTenderListByPurchaseApplyCode(purchaseApplyCode, itemNo);
        return tenderBidDetailConvert.poList2DTOList(bidTenderListByPurchaseApplyCode);
    }

    private TenderBidQuoteLog createQuoteLog(Long bidId,
                                             Long tenderId,
                                             Long bidDetailId,
                                             BigDecimal taxTotalPrice,
                                             BigDecimal taxPrice,
                                             BigDecimal noTaxPrice,
                                             LocalDateTime now)
    {
        return createLog(bidId, tenderId, bidDetailId, taxTotalPrice, taxPrice, noTaxPrice, QUOTE, now);
    }

    private TenderBidQuoteLog createBargainLog(Long bidId,
                                               Long tenderId,
                                               Long bidDetailId,
                                               BigDecimal taxTotalPrice,
                                               BigDecimal taxPrice,
                                               BigDecimal noTaxPrice,
                                               LocalDateTime now)
    {
        return createLog(bidId, tenderId, bidDetailId, taxTotalPrice, taxPrice, noTaxPrice, BARGAIN, now);
    }

    private TenderBidQuoteLog createLog(Long bidId,
                                        Long tenderId,
                                        Long bidDetailId,
                                        BigDecimal taxTotalPrice,
                                        BigDecimal taxPrice,
                                        BigDecimal noTaxPrice,
                                        QuoteLogType type,
                                        LocalDateTime now)
    {
        final TenderBidQuoteLog tenderBidQuoteLog = new TenderBidQuoteLog();
        tenderBidQuoteLog.setTaxTotalPrice(taxTotalPrice);
        tenderBidQuoteLog.setTaxPrice(taxPrice);
        tenderBidQuoteLog.setNoTaxPrice(noTaxPrice);
        tenderBidQuoteLog.setBidId(bidId);
        tenderBidQuoteLog.setTenderId(tenderId);
        tenderBidQuoteLog.setBidDetailId(bidDetailId);
        tenderBidQuoteLog.setType(type.getType());
        tenderBidQuoteLog.setCreateTime(now);
        return tenderBidQuoteLog;
    }

    private void quotationValidation(TenderBid tenderBid, Tender tender) {
        final BigDecimal totalAmount = tenderBid.getFinalCnyAmount();
        final Long bidId = tenderBid.getId();
        final Long tenderId = tender.getId();
        final TenderSettingCompetitive competitive = tenderSettingCompetitiveService.getById(tenderId);
        //规则校验
        /**
         * 报价次数
         */
        final Integer frequency = competitive.getFrequency();
        if (frequency != null && frequency > 0) {
            //取日志表
            Long count = tenderBidQuoteLogService.count(
                    new LambdaQueryWrapper<TenderBidQuoteLog>().eq(TenderBidQuoteLog::getBidId, bidId));
            if (count >= frequency) {
                throw new DaoException("投标次数已用完!");
            }
        }
        /**
         * 降幅判断
         */
        final String reduction = competitive.getReduction();
        if (!reduction.equals("0")) {
            //取日志表
            TenderBidQuoteLog quoteLog = tenderBidQuoteLogService.getOne(
                    new LambdaQueryWrapper<TenderBidQuoteLog>().eq(TenderBidQuoteLog::getBidId, bidId)
                            .orderBy(true, false, TenderBidQuoteLog::getCreateTime), false);
            if (Objects.nonNull(quoteLog)) {
                final BigDecimal lastTaxPrice = quoteLog.getTaxTotalPrice();
                if (reduction.endsWith("%")) {
                    //百分比降幅
                    final BigDecimal bigDecimal = new BigDecimal(reduction.replace("%", StrUtil.EMPTY));
                    final BigDecimal currentRate = BigDecimal.ONE.subtract(
                            bigDecimal.divide(BigDecimal.valueOf(100), 4, BigDecimal.ROUND_HALF_UP));

                    if (lastTaxPrice.multiply(currentRate).compareTo(totalAmount) <= 0) {
                        throw new DaoException(
                                StrUtil.format("不满足竞价降幅!上次总价:{},本次报价:{},降幅:{}", lastTaxPrice,
                                        totalAmount, reduction));
                    }
                } else {
                    //价格降幅
                    final BigDecimal bigDecimal = new BigDecimal(reduction);

                    if (totalAmount.add(bigDecimal).compareTo(lastTaxPrice) >= 0) {
                        throw new DaoException(
                                StrUtil.format("不满足竞价降幅!上次总价:{},本次报价:{},降幅:{}", lastTaxPrice,
                                        totalAmount, bigDecimal));
                    }
                }
            }
        }
        //投标间隔
        final Integer intervalMinute = competitive.getIntervalMinute();
        if (intervalMinute > 0) {
            TenderBidQuoteLog quoteLog = tenderBidQuoteLogService.getOne(
                    new LambdaQueryWrapper<TenderBidQuoteLog>().eq(TenderBidQuoteLog::getBidId, bidId)
                            .orderBy(true, false, TenderBidQuoteLog::getCreateTime), false);

            if (Objects.nonNull(quoteLog) &&
                    quoteLog.getCreateTime().plusMinutes(intervalMinute).isAfter(LocalDateTime.now())) {
                throw new DaoException(StrUtil.format("不满足竞价时间间隔!上次报价时间:{},需要间隔:{}",
                        DateUtils.formatYYYYMMDDHms(quoteLog.getCreateTime()),
                        DateUtils.formatYYYYMMDDHms(LocalDateTime.now())));
            }
        }
        /**
         * 是否延长投标时间
         */
        final Integer delayedMinute = competitive.getDelayedMinute();
        if (delayedMinute > 0) {
            //获取全部报价然后判断当前价格是否是最低价格
            final TenderBid tenderBids = tenderBidService.getOne(
                    new LambdaQueryWrapper<TenderBid>().eq(TenderBid::getTenderId, tenderId)
                            .isNotNull(TenderBid::getFinalCnyAmount).ne(TenderBid::getId, bidId)
                            .orderBy(true, true, TenderBid::getFinalCnyAmount), false);
            if (Objects.nonNull(tenderBids) && totalAmount.compareTo(tenderBids.getFinalCnyAmount()) < 0) {
                //需要延长时间
                final TenderRound tenderRound = tenderRoundService.getOne(
                        new LambdaQueryWrapper<TenderRound>().eq(TenderRound::getTenderId, tenderId), false);
                final LocalDateTime endDay = tenderRound.getEndDay();
                //如果截止时间与当前时间间隔小于30分钟就需要处理延长时间的逻辑
                //获取两个时间的间隔分钟，a在b前面，a跟b的间隔时间是正数
                final LocalDateTime now = LocalDateTime.now();
                final long between = LocalDateTimeUtil.between(now, endDay, ChronoUnit.MINUTES);
                if (0 <= between && between < 30) {
                    final LocalDateTime localDateTime = endDay.plusMinutes(delayedMinute);
                    tenderRound.setEndDay(localDateTime);
                    tenderRoundService.updateById(tenderRound);
                }
            }
        }
        //计算排名
        //获取全部报价然后判断当前价格是否是最低价格
        //需要进行排名
        final List<TenderBid> tenderBids = tenderBidService.list(
                new LambdaQueryWrapper<TenderBid>().eq(TenderBid::getTenderId, tenderId)
                        .isNotNull(TenderBid::getFinalCnyAmount).orderBy(true, true, TenderBid::getFinalCnyAmount));
        // 使用Comparator对tenderBids进行排序
        Collections.sort(tenderBids, Comparator.comparing(TenderBid::getFinalCnyAmount));

        // 设置排名
        int rank = 1;
        BigDecimal previousAmount = BigDecimal.valueOf(Double.MIN_VALUE);
        for (TenderBid bid : tenderBids) {
            if (bid.getFinalCnyAmount().compareTo(previousAmount) != 0) {
                previousAmount = bid.getFinalCnyAmount();
                bid.setRanking(rank);
            }
            rank++;
        }
        tenderBidService.updateBatchById(tenderBids);
    }

    private Pair<BigDecimal, BigDecimal> getCnyPrice(String currency, BigDecimal amount) {
        final BigDecimal exchangeRateDefault = exchangeRateClient.getExchangeRateDefault(currency);
        return Pair.of(exchangeRateDefault, amount.multiply(exchangeRateDefault));
    }

    private BigDecimal getSumPrice(Long bidId) {
        final List<TenderBidDetail> list = tenderBidDetailService.list(
                new LambdaQueryWrapper<TenderBidDetail>().eq(TenderBidDetail::getBidId, bidId));
        AtomicReference<BigDecimal> totalAmount = new AtomicReference<>(BigDecimal.ZERO);
        list.stream().filter(a -> Objects.nonNull(a.getFinalPrice())).forEach(a -> {
            final BigDecimal finalPrice = a.getFinalPrice();
            final BigDecimal count = a.getCount();
            final BigDecimal finalAmount = finalPrice.multiply(count);
            totalAmount.set(totalAmount.get().add(finalAmount));
        });
        return totalAmount.get();
    }

    private BigDecimal getSumCnyTotal(Long bidId) {
        final List<TenderBidDetail> list = tenderBidDetailService.list(
                new LambdaQueryWrapper<TenderBidDetail>().eq(TenderBidDetail::getBidId, bidId));
        AtomicReference<BigDecimal> totalAmount = new AtomicReference<>(BigDecimal.ZERO);
        list.stream().filter(a -> Objects.nonNull(a.getFinalPrice())).forEach(a -> {
            final BigDecimal finalPrice = a.getFinalCnyAmount();//不含税人民币单价
            final BigDecimal count = a.getCount();
            final BigDecimal finalAmount = finalPrice.multiply(count);
            totalAmount.set(totalAmount.get().add(finalAmount));
        });
        return totalAmount.get();
    }
}