package com.jwipc.tender.logic.impl;

import cn.hutool.core.bean.BeanUtil;
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.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
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.api.constant.RestCode;
import com.jwipc.api.dto.ApiResult;
import com.jwipc.api.dto.OaFileParamDTO;
import com.jwipc.constant.DictCode;
import com.jwipc.constant.FlagConst;
import com.jwipc.constant.OaProcessType;
import com.jwipc.constant.SysConfigKey;
import com.jwipc.exception.DaoException;
import com.jwipc.inquiry.client.InquiryClient;
import com.jwipc.inquiry.client.PriceInfoClient;
import com.jwipc.inquiry.dto.CalculateQuotaDTO;
import com.jwipc.inquiry.dto.PriceImportCDTO;
import com.jwipc.inquiry.dto.PriceInfoClientDTO;
import com.jwipc.page.PageUtil;
import com.jwipc.page.PageView;
import com.jwipc.prop.SysProp;
import com.jwipc.security.LoginUtil;
import com.jwipc.supplier.clent.dto.SupplierContactsDTO;
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.BenchmarksLogic;
import com.jwipc.tender.model.po.BargainingStatusPO;
import com.jwipc.tender.model.po.TenderListPO;
import com.jwipc.tender.model.query.TenderQuery;
import com.jwipc.tender.service.*;
import com.jwipc.util.DateUtils;
import com.jwipc.util.EasyExcelImportUtils;
import com.jwipc.util.EncryptUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;


/**
 * <p> </p>
 *
 * @author BOGON
 * @version 1.0
 * @since JDK 1.8
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class BenchmarksLogicImpl implements BenchmarksLogic {
    private final ITenderService tenderService;
    private final ITenderDetailService tenderDetailService;
    private final TenderConvert tenderConvert;
    private final ITenderAttachmentService tenderAttachmentService;
    private final TenderAttachmentConvert tenderAttachmentConvert;
    private final ITenderBidService tenderBidService;
    private final ITenderOpenUserService tenderOpenUserService;
    private final TenderOpenUserConvert tenderOpenUserConvert;
    private final ModelAggregationService modelAggregationService;
    private final ITenderBidDetailService tenderBidDetailService;
    private final ITenderBidDetailExtService tenderBidDetailExtService;
    private final TenderBidDetailConvert tenderBidDetailConvert;
    private final ITenderBidScoreDimensionService bidScoreDimensionService;
    private final FileClient fileClient;
    private final ITenderRoundService tenderRoundService;
    private final SupplierClient supplierClient;
    private final InquiryClient inquiryClient;
    private final PurchaseCategoryClient purchaseCategoryClient;
    private final SysProp sysProp;
    private final TaxClient taxClient;
    private final PriceInfoClient priceInfoClient;
    private final SysConfigClient sysConfigClient;
    private final EmailSendClient emailSendClient;
    private final ITenderApproveService tenderApproveService;
    private final TenderApproveConvert tenderApproveConvert;
    private final TenderBidConvert tenderBidConvert;
    private final UserPurchaseGroupClient userPurchaseGroupClient;
    private final UserClient userClient;
    private final TenderDetailConvert tenderDetailConvert;
    private final DictClient dictClient;

    @Override
    public PageView getList(TenderQuery query) {
        if (StrUtil.isNotEmpty(query.getStatus())) {
            final List<String> statusArray = query.getStatusArray();
            query.setStatusList(statusArray.stream().map(a -> Integer.valueOf(a)).collect(Collectors.toList()));
        } else {
            query.setStatusList(TenderStatus.getBenchmarksStatus());
        }
        final Set<String> userPurchaseGroupCode = userPurchaseGroupClient.getUserPurchaseGroupCode(
                LoginUtil.getUserId());
        query.setUserPurchaseGroupCodes(userPurchaseGroupCode);
        Page<TenderListPO> page = PageUtil.toPage(query);
        query.setLoginUserId(LoginUtil.getUserId());
        List<TenderListPO> list = tenderService.findBenchmarksList(page, query);
        List<TenderBenchmarksListDTO> tenderBenchmarksListDTOS = tenderConvert.pos2Dtos1(list);
        final Set<Long> tenderIds = list.stream().map(TenderListPO::getId).collect(Collectors.toSet());
        //查询议价状态
        if (CollUtil.isNotEmpty(tenderIds)) {
            List<BargainingStatusPO> statusPOS = tenderBidDetailService.findBargainingStatusCount(tenderIds);
            final Map<Long, BargainingStatusPO> cache = statusPOS.stream()
                    .collect(Collectors.toMap(BargainingStatusPO::getTenderId, a -> a));
            tenderBenchmarksListDTOS.forEach(tenderBenchmarksListDTO -> {
                final BargainingStatusPO bargainingStatusPO = cache.get(tenderBenchmarksListDTO.getId());
                if (Objects.nonNull(bargainingStatusPO)) {
                    tenderBenchmarksListDTO.setOk(bargainingStatusPO.getOk());
                    tenderBenchmarksListDTO.setNotOk(bargainingStatusPO.getNotOk());
                }
            });
        }
        return PageUtil.getPV(tenderBenchmarksListDTOS, page.getTotal());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOpenBidStatus(String idStr) {
        final List<String> tenderIds = StrUtil.split(idStr, StrUtil.COMMA);
        if (CollUtil.isEmpty(tenderIds)) {
            throw new DaoException("请选中招标单!");
        }
        //查询本人的确认开标的数据
        final List<TenderOpenUser> list = tenderOpenUserService.list(
                new LambdaQueryWrapper<TenderOpenUser>().in(TenderOpenUser::getTenderId, tenderIds)
                        .eq(TenderOpenUser::getUserId, LoginUtil.getUserId())
                        .eq(TenderOpenUser::getState, TenderOpenUserState.PENDING_CONFIRMATION.getState()));
        if (CollUtil.isEmpty(list)) {
            throw new DaoException("没有需要确认开标的招标单!");
        }
        final List<Tender> tenders = tenderService.listByIds(tenderIds);
        for (Tender tenderRound : tenders) {
            final Integer status = tenderRound.getStatus();
            if (!status.equals(TenderStatus.EXPIRED.getStatus())) {
                throw new DaoException("不是截止状态无法开标无法确认开标!");
            }
        }
        list.stream().

                forEach(a ->

                {
                    a.setTime(LocalDateTime.now());
                    a.setState(TenderOpenUserState.CONFIRMED.getState());
                });
        tenderOpenUserService.updateBatchById(list);
    }

    @Override
    public PageView getQuoteDetails(Long id) {

        final List<TenderBidDetailAllDTO> tenderBidDetailAllDTOS = getTenderBidDetailAllDTOS(id);
        return PageUtil.getPV(tenderBidDetailAllDTOS, tenderBidDetailAllDTOS.size());
    }
    //定标计算查询排名
    @NotNull
    @Override
    public List<TenderBidDetailAllDTO> getTenderBidDetailAllDTOS(Long id) {
        final List<TenderBid> tenderBids = tenderBidService.list(
                new LambdaQueryWrapper<TenderBid>().eq(TenderBid::getTenderId, id));
        final List<TenderDetail> tenderDetails = tenderDetailService.list(
                new LambdaQueryWrapper<>(TenderDetail.class).eq(TenderDetail::getTenderId, id)
                        .orderByAsc(TenderDetail::getRowNo));
        final Map<Long, TenderDetail> tenderDetailMap = new LinkedHashMap<>();
        for (TenderDetail tenderDetail : tenderDetails) {
            tenderDetailMap.put(tenderDetail.getId(), tenderDetail);
        }
        final Map<Long, Integer> rankingMap = tenderBids.stream().filter(a -> Objects.nonNull(a.getRanking()))
                .collect(Collectors.toMap(TenderBid::getId, TenderBid::getRanking));
        final List<Long> bidIds = tenderBids.stream().map(TenderBid::getId).collect(Collectors.toList());
        final Map<Long, BigDecimal> bidTotalAmount = tenderBids.stream()
                .filter(a -> Objects.nonNull(a.getFinalCnyAmount()))
                .collect(Collectors.toMap(TenderBid::getId, TenderBid::getFinalCnyAmount));
        final Map<Long, BigDecimal> bidSumAmount = tenderBids.stream()
                .filter(a -> Objects.nonNull(a.getFinalAmount()))
                .collect(Collectors.toMap(TenderBid::getId, TenderBid::getFinalAmount));
        //取当前的评分数据,如果没有置空
        final List<TenderBidScoreDimension> tenderBidScoreDimensions = bidScoreDimensionService.list(
                new LambdaQueryWrapper<TenderBidScoreDimension>().in(TenderBidScoreDimension::getBidId, bidIds));
        //需要判断是否打分
        boolean hasScore = false;
        if (CollUtil.isNotEmpty(tenderBidScoreDimensions)) {
            hasScore = true;
        }
        //需要做缓存处理
        final Map<Long, List<TenderBidScoreDimension>> detailScoreCache = tenderBidScoreDimensions.stream()
                .filter(a -> a.getDetailId() != 0L)
                .collect(Collectors.groupingBy(TenderBidScoreDimension::getDetailId));
        final Map<Long, List<TenderBidScoreDimension>> bidScoreCache = tenderBidScoreDimensions.stream()
                .filter(a -> a.getDetailId() == 0L)
                .collect(Collectors.groupingBy(TenderBidScoreDimension::getBidId));
        //需要找到供应商信息
        final List<TenderBidDetail> list = tenderBidDetailService.list(
                new LambdaQueryWrapper<TenderBidDetail>().eq(TenderBidDetail::getTenderId, id)
                        .orderByAsc(TenderBidDetail::getRowNo));
        final List<TenderBidDetailAllDTO> tenderBidDetailAllDTOS = tenderBidDetailConvert.entities2AllDtos(list);
        modelAggregationService.processRelationAggregation(tenderBidDetailAllDTOS);
        //目前是根据报价明细的row_no分组排序
        final List<Long> ids = list.stream().map(TenderBidDetail::getId).collect(Collectors.toList());
        final List<TenderBidDetailExt> tenderDetailExts = tenderBidDetailExtService.listByIds(ids);
        final Map<Long, TenderBidDetailExt> extCache = tenderDetailExts.stream().collect(
                Collectors.toMap(TenderBidDetailExt::getId, tenderDetailExt -> tenderDetailExt, (k1, k2) -> k1));
        for (TenderBidDetailAllDTO tenderBidDetailAllDTO : tenderBidDetailAllDTOS) {
            final TenderDetail tenderDetail = tenderDetailMap.get(tenderBidDetailAllDTO.getTenderDetailId());
            if(ObjectUtil.isNotEmpty(tenderDetail)){
                tenderBidDetailAllDTO.setLastBidPrice(tenderDetail.getLastBidPrice());
                final BigDecimal historyPrice = tenderDetail.getHistoryPrice();
                tenderBidDetailAllDTO.setHistoryPrice(historyPrice);
                final TenderBidDetailExt tenderBidDetailExt = extCache.get(tenderBidDetailAllDTO.getId());
                tenderBidDetailConvert.mergeToAllDtos(tenderBidDetailAllDTO, tenderBidDetailExt);
                final Long detailId = tenderBidDetailAllDTO.getId();
                final Long bidId = tenderBidDetailAllDTO.getBidId();
                final List<TenderBidScoreDimension> detailScore = detailScoreCache.get(detailId);
                if (Objects.nonNull(detailScore)) {
                    for (TenderBidScoreDimension tenderBidScoreDimension : detailScore) {
                        setScore(tenderBidDetailAllDTO, tenderBidScoreDimension);
                    }
                }
                final List<TenderBidScoreDimension> bidScore = bidScoreCache.get(bidId);
                if (Objects.nonNull(bidScore)) {
                    for (TenderBidScoreDimension tenderBidScoreDimension : bidScore) {
                        setScore(tenderBidDetailAllDTO, tenderBidScoreDimension);
                    }
                }
                //需要把总不含税人民币报价设置进去
                tenderBidDetailAllDTO.setTotalAmount(bidTotalAmount.getOrDefault(bidId, BigDecimal.ZERO));
                tenderBidDetailAllDTO.setSumAmount(bidSumAmount.getOrDefault(bidId, BigDecimal.ZERO));
                //2025-03-26 设置行总未税金额 和行总含税金额
                BigDecimal bidNumTemp = tenderBidDetailAllDTO.getBidNum();
                if (Objects.isNull(bidNumTemp)) {
                    bidNumTemp = tenderBidDetailAllDTO.getCount();
                }
                tenderBidDetailAllDTO.setDetailSumAmount(tenderBidDetailAllDTO.getFinalPrice() == null ? BigDecimal.ZERO : tenderBidDetailAllDTO.getFinalPrice().multiply(bidNumTemp).setScale(2, RoundingMode.HALF_UP));//含税总价（原币）=最终含税单价（原币） finalPrice *定标数量（bidNum）
                tenderBidDetailAllDTO.setDetailTotalAmount(tenderBidDetailAllDTO.getFinalCnyAmount() == null ? BigDecimal.ZERO : tenderBidDetailAllDTO.getFinalCnyAmount().multiply(bidNumTemp).setScale(2, RoundingMode.HALF_UP));//未税总价(CNY) = 换算后未税单价（CNY)finalCnyAmount *定标数量（bidNum）
                final Integer ranking = rankingMap.get(tenderBidDetailAllDTO.getBidId());
                if (Objects.nonNull(ranking)) {
                    tenderBidDetailAllDTO.setRanking(ranking);
                }
                final BigDecimal bidNum = tenderBidDetailAllDTO.getBidNum();
                if (Objects.isNull(bidNum)) {
                    tenderBidDetailAllDTO.setBidNum(tenderBidDetailAllDTO.getCount());
                }
                //计算降幅
                final BigDecimal finalCnyAmount = tenderBidDetailAllDTO.getFinalCnyAmount();
                if (Objects.nonNull(finalCnyAmount) && Objects.nonNull(historyPrice)) {
                    if (historyPrice.compareTo(BigDecimal.ZERO) != 0) {
                        final String plainString = finalCnyAmount.subtract(historyPrice)
                                .divide(historyPrice, 6, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100))
                                .setScale(2, RoundingMode.DOWN)
                                .stripTrailingZeros().toPlainString();
                        tenderBidDetailAllDTO.setDecline(plainString + "%");
                    }
                }
            }
        }
        //需要分组排序
        final List<TenderBidDetailAllDTO> tenderBidDetailAllDTOS1 = processTenderBidDetails(tenderBidDetailAllDTOS,
                hasScore);
        //需要校验
        final Map<Long, List<TenderBidDetailAllDTO>> collect = tenderBidDetailAllDTOS1.stream()
                .collect(Collectors.groupingBy(TenderBidDetailAllDTO::getBidId));
        for (Long l : collect.keySet()) {
            final List<TenderBidDetailAllDTO> tenderBidDetailAllDTOS2 = collect.get(l);
            BigDecimal detailAmount = tenderBidDetailAllDTOS2.stream()
                    .filter(a -> Objects.nonNull(a.getFinalCnyAmount())).map(dto -> {
                        BigDecimal finalCnyAmount = dto.getFinalCnyAmount();
                        final BigDecimal count = dto.getCount();
                        // 处理finalCnyAmount和count为null的情况
                        if (finalCnyAmount == null || count == null) {
                            return finalCnyAmount;
                        }
                        return finalCnyAmount.multiply(count);
                    })
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            final TenderBidDetailAllDTO tenderBidDetailAllDTO = tenderBidDetailAllDTOS2.get(0);
            detailAmount = detailAmount.setScale(2, RoundingMode.DOWN);
            if (Objects.nonNull(tenderBidDetailAllDTO.getTotalAmount())) {
                if (tenderBidDetailAllDTO.getTotalAmount().compareTo(detailAmount) != 0) {
                    for (TenderBidDetailAllDTO bidDetailAllDTO : tenderBidDetailAllDTOS2) {
                        bidDetailAllDTO.setOk(0);
                    }
                } else {
                    for (TenderBidDetailAllDTO bidDetailAllDTO : tenderBidDetailAllDTOS2) {
                        bidDetailAllDTO.setOk(1);
                    }
                }
            } else {
                for (TenderBidDetailAllDTO bidDetailAllDTO : tenderBidDetailAllDTOS2) {
                    bidDetailAllDTO.setOk(1);
                }
            }

        }
        return tenderBidDetailAllDTOS1;
    }

    @Override
    public PageView getBidFileList(Long tenderId) {
        final Pair<Map<Long, Long>, List<TenderAttachment>> result = getBidTenderAttachemntByTenderId(
                tenderId);
        List<BidAttachmentDTO> attachmentDTOS = tenderAttachmentConvert.entities2Dtos1(result.getValue());
        for (BidAttachmentDTO attachmentDTO : attachmentDTOS) {
            attachmentDTO.setSupplierId(result.getKey().get(attachmentDTO.getMasterId()));
        }
        modelAggregationService.processRelationAggregation(attachmentDTOS);
        return PageUtil.getPV(attachmentDTOS, attachmentDTOS.size());
    }

    private Pair<Map<Long, Long>, List<TenderAttachment>> getBidTenderAttachemntByTenderId(Long tenderId) {
        final List<TenderBid> tenderBids = tenderBidService.list(
                new LambdaQueryWrapper<TenderBid>().eq(TenderBid::getTenderId, tenderId));
        final Map<Long, Long> supplierIdCache = tenderBids.stream()
                .collect(Collectors.toMap(TenderBid::getId, tenderBid -> tenderBid.getSupplierId()));
        final Set<Long> bidIds = tenderBids.stream().map(TenderBid::getId).collect(Collectors.toSet());
        final List<TenderAttachment> tenderAttachments = tenderAttachmentService.list(
                new LambdaQueryWrapper<TenderAttachment>().in(TenderAttachment::getMasterId, bidIds)
                        .in(TenderAttachment::getType, TenderAttachmentType.getBidFileTypes())
                        .orderBy(true, true, TenderAttachment::getMasterId));
        return Pair.of(supplierIdCache, tenderAttachments);
    }

    @Override
    public Pair<String, String> downloadZip(Long tenderId) {
        final Tender tender = tenderService.getById(tenderId);
        final Pair<Map<Long, Long>, List<TenderAttachment>> result = getBidTenderAttachemntByTenderId(
                tenderId);
        final List<TenderAttachment> value = result.getValue();
        final Set<Long> fileIds = value.stream().map(TenderAttachment::getFileId).collect(Collectors.toSet());
        final String zipFileName = StrUtil.format("{}-{}.zip", tender.getTitle(), tender.getCode());
        final String filePath = fileClient.zipFiles(zipFileName, fileIds);
        return Pair.of(filePath, zipFileName);
    }

    @Override
    public List<BenchmarksScoreExportDTO> getExportScoreList(Long tenderId) {
        final List<TenderBid> tenderBids = tenderBidService.list(
                new LambdaQueryWrapper<TenderBid>().eq(TenderBid::getTenderId, tenderId));
        final Map<Long, Long> supplierIdCache = tenderBids.stream()
                .collect(Collectors.toMap(TenderBid::getId, tenderBid -> tenderBid.getSupplierId()));
        final List<TenderBidDetail> bidDetails = tenderBidDetailService.list(
                new LambdaQueryWrapper<TenderBidDetail>().eq(TenderBidDetail::getTenderId, tenderId));
        final Set<Long> collect = bidDetails.stream().map(TenderBidDetail::getId).collect(Collectors.toSet());
        final List<TenderBidDetailExt> bidDetailExts = tenderBidDetailExtService.listByIds(collect);
        final Map<Long, TenderBidDetailExt> extCache = bidDetailExts.stream()
                .collect(Collectors.toMap(TenderBidDetailExt::getId, a -> a));
        List<BenchmarksScoreExportDTO> scoreExportDTOS = tenderBidDetailConvert.entities2ScoreDTO(bidDetails);
        // TODO: 2024/3/4 暂时不需要补充ext数据 jiez
        scoreExportDTOS.stream().forEach(a -> a.setSupplierId(supplierIdCache.get(a.getBidId())));
        modelAggregationService.processRelationAggregation(scoreExportDTOS);
        //需要把分数查询到
        final List<TenderBidScoreDimension> scoreDimensions = bidScoreDimensionService.list(
                new LambdaQueryWrapper<TenderBidScoreDimension>().in(TenderBidScoreDimension::getBidId, collect));
        for (BenchmarksScoreExportDTO scoreExportDTO : scoreExportDTOS) {
            for (TenderBidScoreDimension exportDTO : scoreDimensions) {
                setScore(scoreExportDTO, exportDTO);
            }
        }
        return scoreExportDTOS;
    }

    @Override
    public void importScore(List<BenchmarksScoreExportDTO> scoreExportDTOS, Long tenderId) {
        //校验是否是同一个招标单，防止数据错乱
        final Set<Long> bidIds = scoreExportDTOS.stream().map(BenchmarksScoreExportDTO::getBidId)
                .collect(Collectors.toSet());
        final List<TenderBid> tenderBids = tenderBidService.listByIds(bidIds);
        final Set<Long> tenderIds1 = tenderBids.stream().map(TenderBid::getTenderId).collect(Collectors.toSet());
        if (tenderIds1.size() > 1) {
            throw new DaoException("数据错误无法导入");
        } else if (tenderIds1.size() == 1 && !tenderIds1.contains(tenderId)) {
            throw new DaoException("数据错误无法导入");
        }
        final Set<Long> bidDetailIds = scoreExportDTOS.stream().map(BenchmarksScoreExportDTO::getId)
                .collect(Collectors.toSet());
        final List<TenderBidDetail> tenderBidDetails = tenderBidDetailService.listByIds(bidDetailIds);
        final Set<Long> tenderIds2 = tenderBidDetails.stream().map(TenderBidDetail::getTenderId)
                .collect(Collectors.toSet());
        if (tenderIds2.size() > 1) {
            throw new DaoException("数据错误无法导入");
        } else if (tenderIds2.size() == 1 && !tenderIds2.contains(tenderId)) {
            throw new DaoException("数据错误无法导入");
        }
        //导入分数数据

        List<TenderBidScoreDimension> insertList = Lists.newArrayList();
        for (BenchmarksScoreExportDTO scoreExportDTO : scoreExportDTOS) {
            insertList.addAll(analysisScoreDimension(scoreExportDTO));
        }

        if (CollUtil.isNotEmpty(insertList)) {
            //需要把之前的分数全部删除
            bidScoreDimensionService.remove(
                    new LambdaQueryWrapper<TenderBidScoreDimension>().in(TenderBidScoreDimension::getBidId, bidIds));
            //批量插入
            final List<TenderBidScoreDimension> tenderBidScoreDimensions = calculatePriceScore(bidDetailIds);
            if (CollUtil.isNotEmpty(tenderBidScoreDimensions)) {
                insertList.addAll(tenderBidScoreDimensions);
            }
            bidScoreDimensionService.saveBatch(insertList);
            //需要统计分数
            //需要计算价格分
            final List<TenderBidDetail> tenderBidDetails1 = tenderBidDetailService.listByIds(bidDetailIds);
            List<TenderBidScoreDimension> totalScore = Lists.newArrayList();
            for (TenderBidDetail tenderBidDetail : tenderBidDetails1) {
                final List<TenderBidScoreDimension> bidScoreDimensions = bidScoreDimensionService.list(
                        new LambdaQueryWrapper<TenderBidScoreDimension>()
                                .eq(TenderBidScoreDimension::getBidId, tenderBidDetail.getBidId())
                                .eq(TenderBidScoreDimension::getDetailId, tenderBidDetail.getId()));
                //计算总分数
                BigDecimal totalWeightScore = bidScoreDimensions.stream()
                        .map(TenderBidScoreDimension::getWeightScore)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                totalScore.add(convertToBSD(tenderBidDetail.getBidId(), tenderBidDetail.getId(), totalWeightScore,
                        DimensionCode.DC_TOTAL));
            }
            bidScoreDimensionService.saveBatch(totalScore);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitBargaining(List<SubmitBargainingItemDTO> dto) {
        List<TenderBidDetail> tenderBidDetails = tenderBidDetailConvert.dtos2Entities1(dto);
        Set<Long> bidIds = tenderBidDetails.stream().map(TenderBidDetail::getBidId).collect(Collectors.toSet());
        tenderBidDetails.forEach(a -> a.setBargainingStatus(BargainingStatus.BARGAINING.getValue()));
        tenderBidService.update(
                new LambdaUpdateWrapper<TenderBid>().set(TenderBid::getStatus, BidStatus.BARGAINING.getStatus())
                        .in(TenderBid::getId, bidIds));
        final List<TenderBid> tenderBids = tenderBidService.list(new LambdaQueryWrapper<TenderBid>()
                .in(TenderBid::getId, bidIds));
        tenderBidDetailService.updateBatchById(tenderBidDetails);
        //需要推送议价邮件
        if (CollUtil.isNotEmpty(tenderBids)) {
            final Long tenderId = tenderBids.get(0).getTenderId();
            final Tender tender = tenderService.getById(tenderId);
            sendBargainingEmail(tenderBids, tender);
        }

    }

    private void sendBargainingEmail(List<TenderBid> tenderBids, Tender tender) {
        final Set<Long> supplierIds = tenderBids.stream().map(TenderBid::getSupplierId).collect(Collectors.toSet());
        final List<SupplierDTO> supplierByIds = supplierClient.getSupplierByIds(supplierIds);
        final Map<Long, SupplierDTO> supplierCache = supplierByIds.stream()
                .collect(Collectors.toMap(SupplierDTO::getId, a -> a));
        for (TenderBid tenderBid : tenderBids) {
            final SupplierDTO supplierDTO = supplierCache.get(tenderBid.getSupplierId());
            final String contactsId = tenderBid.getContactsId();
            Map<String, Object> params = Maps.newHashMap();
            params.put("title", tender.getTitle());
            params.put("supplierName", supplierDTO.getSupplierName());
            params.put("publicDate", DateUtils.formatYYYYMMDDDash(LocalDate.now()));
            if (StrUtil.isNotEmpty(contactsId)) {
                final List<String> contactsIds = StrUtil.split(contactsId, StrUtil.COMMA);
                final List<SupplierContactsDTO> contactList = supplierClient.getContactList(contactsIds);
                //获取邮箱
                final Set<String> emails = contactList.stream().filter(a -> StrUtil.isNotEmpty(a.getEmail()))
                        .map(a -> a.getEmail()).collect(Collectors.toSet());
                final EmailTmpSendDTO emailTxtSingleSendDTO = new EmailTmpSendDTO();
                emailTxtSingleSendDTO.setTemplateCode("JW_POST_110");
                emailTxtSingleSendDTO.setParams(params);
                emailTxtSingleSendDTO.setReceiver(emails);
                emailTxtSingleSendDTO.setSender(LoginUtil.getUserId());
                emailSendClient.async(emailTxtSingleSendDTO);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitBenchmarks(SubmitBenchmarksDTO submitBenchmarksDTO) {
        Tender tender = tenderConvert.dto2Entity1(submitBenchmarksDTO);
        final Tender db = tenderService.getById(tender.getId());
        if (Objects.isNull(db)) {
            throw new DaoException("招标单不存在");
        }
        if (!db.getStatus().equals(TenderStatus.REJECT.getStatus()) &&
                !db.getStatus().equals(TenderStatus.EXPIRED.getStatus()) && !db.getStatus().equals(TenderStatus.BIDING_APPR_REJECT.getStatus())) {
            throw new DaoException("招标单状态不正确，无法定标");
        }


//        if (db.getStatus() != TenderStatus.EXPIRED.getStatus()) {
//            throw new DaoException("当前状态无法定标");
//        }
        tender.setBidedFileIds(submitBenchmarksDTO.getBidedFileIds());
        final List<TenderBidDetailAllDTO> tenderBidDetailAllDTOS = submitBenchmarksDTO.getBidDetails();
        //中标的投标单ID
        final Set<Long> bidIds = tenderBidDetailAllDTOS.stream()
                .filter(a -> Objects.equals(a.getStatus(), FlagConst.Y.getCode())).map(TenderBidDetailAllDTO::getBidId)
                .collect(
                        Collectors.toSet());

        Map<Long,String> cachesSuperlier = Maps.newHashMap();
        if (CollUtil.isNotEmpty(bidIds)) {
            tenderBidService
                    .update(new LambdaUpdateWrapper<TenderBid>()
                            .set(TenderBid::getStatus, BidStatus.WON.getStatus())
                            .set(TenderBid::getBidStatus, FlagConst.Y.getMsg())
                            .in(TenderBid::getId, bidIds));
            //需要配判断供应上是否存在erp编码
            final List<TenderBid> tenderBids = tenderBidService.listByIds(bidIds);
            final Set<Long> supplierIds = tenderBids.stream().map(TenderBid::getSupplierId).collect(Collectors.toSet());
            final List<SupplierDTO> supplierByIds = supplierClient.getSupplierByIds(supplierIds);
            final String collect = supplierByIds.stream().filter(a -> StrUtil.isEmpty(a.getSupplierErpCode()))
                    .map(SupplierDTO::getSupplierName).collect(
                            Collectors.joining(StrUtil.COMMA));
            if (StrUtil.isNotEmpty(collect)) {
                throw new DaoException(collect + "是潜在供应商,无法定价!");
            }
            //检查是否为待淘汰供应商
            List<SupplierDTO> collectEliminate = supplierByIds.stream().filter(a -> !Objects.isNull(a.getEliminateFlag()) && a.getEliminateFlag() == 1).collect(Collectors.toList());
            if(CollUtil.isNotEmpty(collectEliminate)){
                StringBuilder supplierCodes = new StringBuilder();
                for (SupplierDTO dto : collectEliminate) {
                    supplierCodes.append(dto.getSupplierCode()).append(",");
                }
                throw new DaoException(StrUtil.format("供应商{}为待淘汰状态，不能生成价格信息，不允许定价",supplierCodes.toString()));
            }
            //把供应商为自购原材料厂商取出来，并取出有效期的月份，一般为6个月，如果没有的默认为12个月，一年。 邓京术 2025-04-02
            if (Objects.equals(submitBenchmarksDTO.getSubmitFlag(), FlagConst.Y.getCode())) {
                List<DictDTO> dictDTOS = dictClient.getDicByType("supplier_material_quote_expiredate");
                DictDTO dictDTO = dictDTOS.get(0);
                tenderBids.stream().forEach(tenderBid -> {
                    for (SupplierDTO supplierDTO : supplierByIds) {
                        if (tenderBid.getSupplierId().equals(supplierDTO.getId()) && StringUtils.isNotEmpty(supplierDTO.getAttr()) && dictDTO.getName().equals(supplierDTO.getAttr())) {
                            //如果字典的类型配置与供应商类型相等，则取出有效期月份
                            cachesSuperlier.put(tenderBid.getId(), dictDTO.getCode());
                            break;
                        }
                    }
                });
            }
        }
        final List<TenderBid> dbTB = tenderBidService.list(
                new LambdaQueryWrapper<>(TenderBid.class).eq(TenderBid::getTenderId, tender.getId()).eq(TenderBid::getAgreeFlag,1).ne(TenderBid::getStatus,3));
        //需要判断是否需要评分，然后判断是否评分
        final TenderRound tenderRound = tenderRoundService.getOne(
                new LambdaQueryWrapper<>(TenderRound.class).eq(TenderRound::getTenderId, tender.getId()), false);
        if (Objects.equals(submitBenchmarksDTO.getSubmitFlag(), FlagConst.Y.getCode())) {
            if (tenderRound.getScoreFlag().equals(FlagConst.Y.getCode())) {
                //需要查询是否存在评分数据，如果不存在需要提示
                final Set<Long> collect = dbTB.stream().map(TenderBid::getId).collect(Collectors.toSet());
                final long count = bidScoreDimensionService.count(
                        new LambdaQueryWrapper<>(TenderBidScoreDimension.class)
                                .in(TenderBidScoreDimension::getBidId, collect));
                if (count <= 0L) {
                    throw new DaoException("未评分无法定标!");
                }
            }
        }

        final Set<Long> noWonBid = dbTB.stream().filter(a -> !bidIds.contains(a.getId())).map(TenderBid::getId)
                .collect(Collectors.toSet());
        if (CollUtil.isNotEmpty(noWonBid)) {
            tenderBidService
                    .update(new LambdaUpdateWrapper<TenderBid>()
                            .set(TenderBid::getStatus, BidStatus.LOST.getStatus())
                            .set(TenderBid::getBidStatus, FlagConst.N.getMsg())
                            .in(TenderBid::getId, noWonBid));
        }


        final String noOaPurchaseCategoryCode = sysConfigClient.getTextConfig(
                SysConfigKey.INQUIRY_FIX_NO_OA_PURCHASE_CATEGORY_CODE.getCode());
        if (!noOaPurchaseCategoryCode.contains(tender.getPurchaseCategoryCode())) {
            //判断中标的配额是否是100
            final List<TenderBidDetail> wonBids = tenderBidDetailService.list(
                    new LambdaQueryWrapper<TenderBidDetail>().eq(TenderBidDetail::getTenderId, tender.getId())
                            .eq(TenderBidDetail::getStatus, FlagConst.Y.getCode()));
            final Map<Long, List<TenderBidDetail>> collect = wonBids.stream()
                    .collect(Collectors.groupingBy(TenderBidDetail::getTenderDetailId));
            for (Long l : collect.keySet()) {
                final List<TenderBidDetail> tenderBidDetails1 = collect.get(l);
                BigDecimal totalQuota = tenderBidDetails1.stream()
                        .map(TenderBidDetail::getQuota)
                        .filter(Objects::nonNull) // Filter out null values to avoid NullPointerException
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                if (totalQuota.compareTo(BigDecimal.valueOf(100)) != 0) {
                    throw new DaoException(tenderBidDetails1.get(0).getMaterialName() + "配额不是100%");
                }
            }
        }
        if (Objects.equals(submitBenchmarksDTO.getSubmitFlag(), FlagConst.Y.getCode())) {
            final List<TenderBidDetail> tenderBidDetails = tenderBidDetailConvert.dtos2Entities(tenderBidDetailAllDTOS);
            // TODO: 2024/3/4 校验中标的数据总价和行汇总价格是否一致 jiez
            if (CollUtil.isNotEmpty(tenderBidDetails)) {
                //设置报价价格的起止时间 2025-04-02 邓京术  自购供应商
                tenderBidDetails.stream().forEach(item -> {
                    if (StringUtils.isNotEmpty(cachesSuperlier.get(item.getBidId()))) {
                        LocalDate priceValidityDate = item.getPriceStartDate();
                        if (!Objects.isNull(priceValidityDate)) {
                            LocalDate plusOneMonth = priceValidityDate.plusMonths(Integer.parseInt(cachesSuperlier.get(item.getBidId())));
                            //如果价格有效期截止时间少于=于，最后日期，则不修改。
                            if (!plusOneMonth.isBefore(item.getPriceValidityDate())) {
                                item.setPriceValidityDate(plusOneMonth);
                            }
                        }
                    } else {
                        LocalDate priceValidityDate = item.getPriceStartDate();
                        if (!Objects.isNull(priceValidityDate)) {
                            LocalDate plusOneMonth = priceValidityDate.plusMonths(12);
                            //如果价格有效期截止时间少于=于，最后日期，则不修改。
                            if (!plusOneMonth.isBefore(item.getPriceValidityDate())) {
                                item.setPriceValidityDate(plusOneMonth);
                            }
                        }
                    }
                });
                tenderBidDetailService.updateBatchById(tenderBidDetails,300);
            }

            tender.setUpdateId(LoginUtil.getUserId());
            tender.setUpdateTime(LocalDateTime.now());
            //需要判断
            //需要推送数据到OA
            //无需oa审批的采购品类
            if ( "1".equals(tender.getTendType())) {
                //呆滞销售招标在定标后，不抛OA审批，招标状态变成"已完成”
                tender.setStatus(TenderStatus.COMPLETED.getStatus());
            } else if (  tender.getPurchaseCategoryCode().equals(noOaPurchaseCategoryCode)) {
                tender.setStatus(TenderStatus.SUPERVISOR_CONFIRMATION.getStatus());
            } else {
                this.pushToOa(tender.getId());
            }
        }
        tenderService.updateById(tender);
        //这里需要考虑下定标时的暂存和真正的定标区别

        //2025-03-11 只要定标成功就将计算的排名和总金额修改到表中
        if(Objects.nonNull(submitBenchmarksDTO.getSubmitFlag()) && submitBenchmarksDTO.getSubmitFlag().intValue() == 1){
            updateTenderBidRanking(tender.getId());
        }


    }

    private void updateTenderBidRanking(Long id){
        final List<TenderBid> tenderBids = tenderBidService.list(
                new LambdaQueryWrapper<>(TenderBid.class).eq(TenderBid::getTenderId, id).eq(TenderBid::getAgreeFlag,1)
                        .gt(TenderBid::getFinalCnyAmount, BigDecimal.ZERO)
                        .isNotNull(TenderBid::getFinalCnyAmount)
                        .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(CollectionUtils.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);
                        //设置价格，计算公式 转换后价格 * 投标数量 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;
            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++;
            }
        }
        //定价操作时，如果没有对应价格信息时，需要提醒页面先进行价格维护，否则就不允许定价
        //这里需要考虑几个场景
        //当用户进行定价时将总价和排名一起修改，定价后的几个状态就直接查询修改后表格里的数据
        //如果时定价前的几个状态，只需要临时查询计算，不要修改表里的数据
        // Sorting the list
        tenderBids.sort(Comparator.comparing(
                TenderBid::getRanking,
                Comparator.nullsLast(Comparator.naturalOrder())
        ));
        //修改排名和CNY不含税总价
        tenderBidService.updateBatchById(tenderBids);
    }


    private void pushToOa(Long id) {
        //需要组装数据推送OA
        final List<TenderBidDetail> list = tenderBidDetailService.list(new LambdaQueryWrapper<>(TenderBidDetail.class)
                .eq(TenderBidDetail::getTenderId, id)
                .eq(TenderBidDetail::getStatus, FlagConst.Y.getCode())
                .orderByAsc(TenderBidDetail::getMaterialCode));
        final List<TenderDetail> tenderDetails = tenderDetailService.list(
                new LambdaQueryWrapper<>(TenderDetail.class).eq(TenderDetail::getTenderId, id));
        final Map<Long, TenderDetail> detailCache = tenderDetails.stream().collect(
                Collectors.toMap(TenderDetail::getId, tenderDetail -> tenderDetail, (k1, k2) -> k1));
        if (CollUtil.isNotEmpty(list)) {
            final Set<Long> collect = list.stream().map(TenderBidDetail::getId).collect(Collectors.toSet());
            final List<TenderBidDetailExt> bidDetailExts = tenderBidDetailExtService.listByIds(collect);

            final Map<Long, TenderBidDetailExt> extCache = bidDetailExts.stream().collect(
                    Collectors.toMap(TenderBidDetailExt::getId, tenderDetailExt -> tenderDetailExt, (k1, k2) -> k1));
            final List<TenderBidDetailAllDTO> tenderBidDetailAllDTOS = tenderBidDetailConvert.entities2AllDtos(list);
            for (TenderBidDetailAllDTO tenderBidDetailAllDTO : tenderBidDetailAllDTOS) {
                final TenderBidDetailExt tenderBidDetailExt = extCache.get(tenderBidDetailAllDTO.getId());
                tenderBidDetailConvert.mergeToAllDtos(tenderBidDetailAllDTO, tenderBidDetailExt);
            }
            final Set<Long> supplierIds = list.stream().map(TenderBidDetail::getSupplierId).collect(Collectors.toSet());
            final List<SupplierDTO> supplierByIds = supplierClient.getSupplierByIds(supplierIds);
            //获取供应商信息
            final Map<Long, SupplierDTO> supplierCodeCache = supplierByIds.stream()
                    .collect(Collectors.toMap(SupplierDTO::getId, a -> a));
            final Tender tender = tenderService.getById(id);
            PriceImportCDTO priceImportDTO = new PriceImportCDTO();
            //组装头
            PriceImportCDTO.Head head = new PriceImportCDTO.Head();
            head.setFd_srmType(String.valueOf(OaProcessType.TENDER_PRICE.getType()));
            //OA流程ID
            head.setProcessId(StringUtils.isNotEmpty(tender.getOaWorkId()) ? tender.getOaWorkId() : "");
            head.setFd_caigouLeiXing("招标");
            //申请人工号
            head.setFd_shenQingRenXingMing(userClient.getByUserId(tender.getCreateId()).getAccountName());
//            head.setFd_shenQingRenXingMing(LoginUtil.getAccount());
            //SRM单号
            head.setFd_shenQingDanHao(tender.getCode());
            //器件分类
            PurchaseCategoryClientDTO purchaseCategoryClientDTO = purchaseCategoryClient.getByCode(
                    tender.getPurchaseCategoryCode());
            head.setFd_qiJianFenLei(ObjectUtil.isNotNull(
                    purchaseCategoryClientDTO) ? purchaseCategoryClientDTO.getFirstLevelPurchaseName() : "");
            //是否现货
            head.setFd_isXianHuo(tender.getSpotFlag() == 1 ? "是" : "否");
            //事业部是否同意
            head.setFd_shiYeBuShiFuTongYi(
                    tender.getBusinessAgreeFlag() == null ? "" : tender.getBusinessAgreeFlag() == 1 ? "是" : "否");
            //客户是否承担
            head.setFd_keHuShiFuChengDan(
                    tender.getCustomerBearFlag() == null ? "" : tender.getCustomerBearFlag() == 1 ? "是" : "否");
            //承担比例%
            head.setFd_chengDanBiLi(tender.getBearRatio() == null ? "" : tender.getBearRatio().toPlainString());
            List<OaFileParamDTO> fileList = new ArrayList<>();
            String encryptHex = EncryptUtils.encrypt(String.valueOf(tender.getId()));
            OaFileParamDTO build = OaFileParamDTO.builder().fileName("详情页面地址")
                    .url(sysProp.getHostUrl() + "/pc/tender/benchmarks/page/detail?type=0&id=" + encryptHex).build();
            fileList.add(build);
            //详情页面地址
            head.setFd_pageUrl(JSON.toJSONString(fileList));
            //备注
            head.setFd_beiZhu(StringUtils.isNotEmpty(tender.getBidRemark()) ? tender.getBidRemark() : "");
            //定价附件
            if (StringUtils.isNotEmpty(tender.getBidedFileIds())) {
                final List<String> split = StrUtil.split(tender.getBidedFileIds(), StrUtil.COMMA);
                final List<FileResposePreviewCDTO> previewUrl = fileClient.getPreviewUrl(split);
                if (CollUtil.isNotEmpty(previewUrl)) {
                    List<OaFileParamDTO> fileParamDTOS = Lists.newArrayList();
                    for (FileResposePreviewCDTO fileResposePreviewDTO : previewUrl) {
                        fileParamDTOS.add(OaFileParamDTO.builder().fileName(fileResposePreviewDTO.getFileName())
                                .url(fileResposePreviewDTO.getPreviewUrl()).downloadUrl(
                                        fileResposePreviewDTO.getDownloadUrl()).build());
                    }
                    head.setFd_fuJian(JSON.toJSONString(fileParamDTOS));
                }
            } else {
                head.setFd_fuJian("");
            }

            priceImportDTO.setHead(head);

            //组装明细
            List<TaxDTO> taxDTOS = taxClient.getAll();
            final Map<String, TaxDTO> taxCache = taxDTOS.stream().collect(Collectors.toMap(TaxDTO::getTaxCode, a -> a));
            PriceImportCDTO.ItemHead itemHead = new PriceImportCDTO.ItemHead();
            List<PriceImportCDTO.ItemHead.Item> itemList = new ArrayList<>();
            tenderBidDetailAllDTOS.forEach(bidDetail -> {
                PriceImportCDTO.ItemHead.Item item = new PriceImportCDTO.ItemHead.Item();
                //供应商代码
                final Long supplierId = bidDetail.getSupplierId();
                final SupplierDTO supplierDTO = supplierCodeCache.get(supplierId);
                item.setFd_gongYingShangDaiMa(supplierDTO.getSupplierCode());
                //供应商名称
                item.setFd_gongYingShangMingChen(supplierDTO.getSupplierName());
                //物料编码
                item.setFd_wuLiaoBianMa(bidDetail.getMaterialCode());
                //描述
                item.setFd_miaoShu(bidDetail.getMaterialName());
                //物料单位
                item.setFd_jgdw(bidDetail.getUnit());
                //税码（SAP代码）
                item.setFd_shuiMa(bidDetail.getTaxRateCode());
                TaxDTO taxDTO = taxCache.get(bidDetail.getTaxRateCode());
                //税码值
                item.setFd_shuiMaZhi(taxDTO.getTaxValue().multiply(BigDecimal.valueOf(100)).toPlainString());
                //币种
                item.setFd_biZhong(bidDetail.getCurrency());
                //含税价格(PCS)
                item.setFd_hsj(bidDetail.getFinalPrice().stripTrailingZeros().toPlainString());
                //换算后价格 未税*汇率
                item.setFd_huanSuanHouJiaGe(
                        bidDetail.getFinalNoTaxPrice()
                                .multiply(bidDetail.getExchangeRate()).stripTrailingZeros()
                                .toPlainString());
                final BigDecimal bigDecimal = new BigDecimal(
                        bidDetail.getFinalPrice().stripTrailingZeros().toPlainString());
                final int scale = bigDecimal.scale();
                if (scale > 2) {
//                final int i = scale - 2;
//                final BigDecimal priceUnit = BigDecimal.TEN.pow(i);
//                final BigDecimal multiply = bigDecimal.multiply(priceUnit);

                    //价格单位(转换SAP)
                    if (scale <= 5) {
                        item.setFd_danWei("1000");
                        //含税价格(转换SAP)
                        item.setFd_jiaGe(
                                bigDecimal.multiply(new BigDecimal("1000")).stripTrailingZeros().toPlainString());
                    }
                    if (scale > 5) {
                        item.setFd_danWei("10000");
                        item.setFd_jiaGe(
                                bigDecimal.multiply(new BigDecimal("10000")).stripTrailingZeros().toPlainString());
                    }
                } else {
                    //含税价格(转换SAP)
                    item.setFd_jiaGe(bidDetail.getFinalPrice().stripTrailingZeros().toPlainString());
                    //价格单位(转换SAP)
                    item.setFd_danWei("1");
                }
                //汇率
                item.setFd_huiLv(bidDetail.getExchangeRate().toPlainString());
                //有效期起始日  这个地方要改
                item.setFd_youXiaoQiQiShiRi(
                        LocalDateTimeUtil.format(bidDetail.getPriceStartDate(), "yyyy-MM-dd"));
                item.setFd_zhongZhiRiQi(
                        LocalDateTimeUtil.format(bidDetail.getPriceValidityDate(), "yyyy-MM-dd"));
                //配额
                item.setFd_peiE(bidDetail.getQuota().stripTrailingZeros().toPlainString());
                //备注
                item.setFd_beiZhu(StringUtils.isNotEmpty(bidDetail.getBidRemark()) ? bidDetail.getBidRemark() : "");
                //信息类别0标准2寄售3外协
                item.setFd_xinXiLeiBie(tender.getPriceType());
                //关税(没有就填0)
                item.setFd_guanShui(BigDecimal.ZERO.stripTrailingZeros().toPlainString());
                //采购组织代码
                item.setFd_suoShuZuZhi(tender.getPurchaseOrgCode());
                //工厂代码
                item.setFd_gongChangCode(bidDetail.getFactoryCode());
                //制造商型号
                item.setFd_zhiZaoXingHao(StringUtils.isNotEmpty(bidDetail.getMpn()) ? bidDetail.getMpn() : "");
                //制造商中文
                item.setFd_zhiZaoShang(
                        StringUtils.isNotEmpty(bidDetail.getManufacturer()) ? bidDetail.getManufacturer() : "");
                //制造商英文
                item.setFd_zhiZaoYingWen(
                        StringUtils.isNotEmpty(bidDetail.getManufacturerEn()) ? bidDetail.getManufacturerEn() : "");
                //国系
                if (StringUtils.isNotEmpty(bidDetail.getCountryName())){
                    DictDTO dictDTO = dictClient.getDictByTypeAndCode(DictCode.COUNTRY_SERIES.getCode(),bidDetail.getCountryName());
                    item.setFd_guoXi(dictDTO.getName());
                }else {
                    item.setFd_guoXi(StringUtils.isNotEmpty(bidDetail.getCountryName())?bidDetail.getCountryName():"");
                }
                //计划交货时间（L/T）
                item.setFd_jiaoHuoShiJian(
                        bidDetail.getK21() == null ? "" : bidDetail.getK21().stripTrailingZeros().toPlainString());
                //最新订单数量（MOQ）
                item.setFd_shuLiang(String.valueOf(bidDetail.getMoq()));
                //最小包装量（MPQ）
                item.setFd_zuiXiaoBaoZhuangLiang(String.valueOf(bidDetail.getMpq()));
                //阶梯数量1
                item.setFd_jieTiShuLiang1("");
                //阶梯价格1
                item.setFd_jieTiJiaGe1("");
                //阶梯数量2
                item.setFd_jieTiShuLiang2("");
                //阶梯价格2
                item.setFd_jieTiJiaGe2("");
                //阶梯数量3
                item.setFd_jieTiShuLiang3("");
                //阶梯价格3
                item.setFd_jieTiJiaGe3("");
                //阶梯数量4
                item.setFd_jieTiShuLiang4("");
                //阶梯价格4
                item.setFd_jieTiJiaGe4("");
                //阶梯数量5
                item.setFd_jieTiShuLiang5("");
                //阶梯价格5
                item.setFd_jieTiJiaGe5("");
                //状态
                item.setFd_zhuangTai("导入");

                final TenderDetail tenderDetail = detailCache.get(bidDetail.getTenderDetailId());
                final PriceInfoClientDTO priceInfoDTO = priceInfoClient.getById(tenderDetail.getLastBidPriceId());
                if (Objects.nonNull(priceInfoDTO)) {
                    //是否新料
                    item.setFd_shiFuXinLiao("否");
                    //上次价格币种
                    item.setFd_shangCiJiaGeBiZhong(priceInfoDTO.getCurrencyCode());
                    //上次汇率
                    item.setFd_shangCiHuiLv(priceInfoDTO.getExchangeRate().toPlainString());
                    //上次税码
                    item.setFd_shangCiShuiMa(priceInfoDTO.getTaxCode());
                    //上次价格原价
                    item.setFd_shangCiJiaGeYuanJia(priceInfoDTO.getTaxPrice().stripTrailingZeros().toPlainString());
                    //上次未税价格
                    item.setFd_shangCiWeiShuiJiaGe(priceInfoDTO.getNoTaxPrice().stripTrailingZeros().toPlainString());
                    //差价（RMB） $明细表1.是否新料$ * ($明细表1.换算后价格$ - $明细表1.上次未税价格$ )
                    final BigDecimal chajia = new BigDecimal(item.getFd_huanSuanHouJiaGe()).subtract(
                            priceInfoDTO.getNoTaxPrice());
                    item.setFd_chaJia(chajia.stripTrailingZeros().toPlainString());
                    if (priceInfoDTO.getNoTaxPrice().compareTo(BigDecimal.ZERO) == 0) {
                        //涨幅 $明细表1.是否新料$ * 100 * $明细表1.差价（RMB）$ / $明细表1.上次未税价格$
                        item.setFd_zhangFu("100");
                    } else {
                        //涨幅 $明细表1.是否新料$ * 100 * $明细表1.差价（RMB）$ / $明细表1.上次未税价格$
                        item.setFd_zhangFu(
                                chajia.divide(priceInfoDTO.getNoTaxPrice(), 2, RoundingMode.HALF_UP)
                                        .stripTrailingZeros()
                                        .toPlainString());
                    }

                } else {
                    //是否新料
                    item.setFd_shiFuXinLiao("是");
                    //上次价格币种
                    item.setFd_shangCiJiaGeBiZhong("");
                    //上次汇率
                    item.setFd_shangCiHuiLv("");
                    //上次税码
                    item.setFd_shangCiShuiMa("");
                    //上次价格原价
                    item.setFd_shangCiJiaGeYuanJia("");
                    //上次未税价格
                    item.setFd_shangCiWeiShuiJiaGe("");
                    //差价（RMB）
                    item.setFd_chaJia("");
                    //涨幅
                    item.setFd_zhangFu("");
                }
                //潮湿等级
                item.setFd_chaoShiDengJi(StringUtils.isNotEmpty(bidDetail.getMsl()) ? bidDetail.getMsl() : "");
                //取消窗
                item.setFd_quXiaoChuang(
                        StringUtils.isNotEmpty(bidDetail.getCancelWindow()) ? bidDetail.getCancelWindow() : "");
                //推迟窗
                item.setFd_tuiChiChuang(
                        StringUtils.isNotEmpty(bidDetail.getDelayWindow()) ? bidDetail.getDelayWindow() : "");
                //ECCN码
                item.setFd_eCCNMa(StringUtils.isNotEmpty(bidDetail.getEccn()) ? bidDetail.getEccn() : "");
                //风险等级
                item.setFd_fengXianDengJi(
                        StringUtils.isNotEmpty(bidDetail.getRiskLevel()) ? bidDetail.getRiskLevel() : "");
                //物流代理费
                if (bidDetail.getCurrency().equals("USD")) {
                    item.setFd_daiLiFei("0.3");
                } else {
                    item.setFd_daiLiFei("0");
                }
                itemList.add(item);
            });
            itemHead.setItem(itemList);
            priceImportDTO.setItem(itemHead);
            final ApiResult<String> orUpdate = priceInfoClient.pushToOa(priceImportDTO);
            final String code = orUpdate.getCode();
            final String oaId = orUpdate.getData();
            final String message = orUpdate.getMessage();
            final LambdaUpdateWrapper<Tender> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(Tender::getId, id);
            updateWrapper.set(Tender::getOaResult, message);
            updateWrapper.set(Tender::getOaTime, LocalDateTime.now());
            if (code.equals(RestCode.SAP_SUCCESS.getCode())) {
                updateWrapper.set(Tender::getStatus, TenderStatus.BIDING_APPR.getStatus());
                updateWrapper.set(Tender::getOaWorkId, oaId);
                tenderService.update(updateWrapper);
                //更新所有询价供应商状态为询价结束
            } else {
                throw new DaoException("OA接口报错：" + orUpdate.getMessage());
            }
        }
    }

    @Override
    public PageView getConfirmUserList(Long tenderId) {
        final List<TenderOpenUser> list = tenderOpenUserService.list(new LambdaQueryWrapper<TenderOpenUser>()
                .eq(TenderOpenUser::getTenderId, tenderId));
        final List<TenderOpenUserDTO> tenderOpenUserDTOS = tenderOpenUserConvert.entities2Dtos(list);
        modelAggregationService.processRelationAggregation(tenderOpenUserDTOS);
        return PageUtil.getPV(tenderOpenUserDTOS);
    }

    private List<TenderBidScoreDimension> analysisScoreDimension(BenchmarksScoreExportDTO scoreExportDTO) {
        List<TenderBidScoreDimension> insertList = Lists.newArrayList();
        insertList.add(convertToBSD(scoreExportDTO.getBidId(),
                scoreExportDTO.getId(),
                scoreExportDTO.getDcTouch(),
                DimensionCode.DC_TOUCH));
        insertList.add(convertToBSD(scoreExportDTO.getBidId(),
                scoreExportDTO.getId(),
                scoreExportDTO.getDcBusiness(),
                DimensionCode.DC_BUSINESS));
        return insertList;
    }

    public TenderBidScoreDimension convertToBSD(Long bidId, Long detailId, BigDecimal score,
                                                DimensionCode dimensionCode)
    {
        TenderBidScoreDimension scoreDimension = new TenderBidScoreDimension();
        scoreDimension.setBidId(bidId);
        scoreDimension.setDetailId(detailId);
        scoreDimension.setDimensionCode(dimensionCode.getCode());
        scoreDimension.setTotalScore(score);
        //需要计算权重分
        final BigDecimal weight = dimensionCode.getWeight();
        scoreDimension.setWeightScore(score.multiply(weight));
        return scoreDimension;
    }

    private void setScore(TenderScoreDTO tenderBidDetailAllDTO,
                          TenderBidScoreDimension tenderBidScoreDimension)
    {
        final DimensionCode byCode = DimensionCode.getByCode(tenderBidScoreDimension.getDimensionCode());
        switch (byCode) {
            case DC_PRICE -> tenderBidDetailAllDTO.setDcPrice(tenderBidScoreDimension.getTotalScore());
            case DC_TOUCH -> tenderBidDetailAllDTO.setDcTouch(tenderBidScoreDimension.getTotalScore());
            case DC_BUSINESS -> tenderBidDetailAllDTO.setDcBusiness(tenderBidScoreDimension.getTotalScore());
            case DC_TOTAL -> tenderBidDetailAllDTO.setDcTotal(tenderBidScoreDimension.getTotalScore());
        }
    }


    public List<TenderBidDetailAllDTO> processTenderBidDetails(List<TenderBidDetailAllDTO> tenderBidDetailAllDTOS,
                                                               boolean hasScore)
    {
        List<TenderBidDetailAllDTO> result = Lists.newArrayList();
        // 根据 tenderDetailId 分组
        Map<Long, List<TenderBidDetailAllDTO>> groupedByTenderDetailId = new LinkedHashMap<>();
        for (TenderBidDetailAllDTO tenderBidDetailAllDTO : tenderBidDetailAllDTOS) {
            List<TenderBidDetailAllDTO> tenderBidDetailAllDTOS1 = groupedByTenderDetailId.get(
                    tenderBidDetailAllDTO.getTenderDetailId());
            if (CollUtil.isEmpty(tenderBidDetailAllDTOS1)) {
                tenderBidDetailAllDTOS1 = Lists.newArrayList();
                tenderBidDetailAllDTOS1.add(tenderBidDetailAllDTO);
                groupedByTenderDetailId.put(tenderBidDetailAllDTO.getTenderDetailId(), tenderBidDetailAllDTOS1);
            } else {
                groupedByTenderDetailId.get(
                        tenderBidDetailAllDTO.getTenderDetailId()).add(tenderBidDetailAllDTO);
            }

        }
        // 遍历每个分组进行排序和排名
        groupedByTenderDetailId.forEach((tenderDetailId, details) -> {
            List<TenderBidDetailAllDTO> tmp = Lists.newArrayList();
            if (hasScore) {
                List<TenderBidDetailAllDTO> scoreRank = details.stream()
                        .filter(detail -> Objects.equals(detail.getOfferStatus(), FlagConst.Y.getCode()) &&
                                Objects.nonNull(detail.getDcTotal()))
                        .sorted(Comparator.comparing(TenderBidDetailAllDTO::getDcTotal,
                                Comparator.nullsLast(Comparator.reverseOrder()))).toList();

                // 按照规则排序并设置排名

                int rank = 0;
                BigDecimal prevDcTotal = null;
                for (TenderBidDetailAllDTO detail : scoreRank) {
                    if (Objects.isNull(detail.getDcTotal())) {
                        continue;
                    }
                    if (!detail.getDcTotal().equals(prevDcTotal)) {
                        rank++;
                    }
                    detail.setBenchmarksRank(rank);
                    prevDcTotal = detail.getDcTotal();
                    tmp.add(detail);
                }
            } else {
                List<TenderBidDetailAllDTO> priceRank = details.stream()
//                        .filter(detail -> detail.getOfferStatus() == 1)
                        .sorted(Comparator.comparing(
                                a -> Objects.nonNull(a.getFinalCnyAmount()) ? a.getFinalCnyAmount() : BigDecimal.ZERO))
                        .toList();
                // 按照规则排序并设置排名
                int rank1 = 0;
                BigDecimal prevTaxPrice = null;
                for (TenderBidDetailAllDTO detail : priceRank) {
                    if (Objects.isNull(detail.getFinalCnyAmount()) ||
                            detail.getFinalCnyAmount().compareTo(BigDecimal.ZERO) == 0) {
                        detail.setBenchmarksRank(999999);
                        tmp.add(detail);
                        continue;
                    }
                    if (!detail.getFinalCnyAmount().equals(prevTaxPrice)) {
                        rank1++;
                    }
                    detail.setBenchmarksRank(rank1);
                    prevTaxPrice = detail.getFinalCnyAmount();
                    tmp.add(detail);

                }
            }
            tmp.sort(Comparator.comparing(TenderBidDetailAllDTO::getBenchmarksRank));
            result.addAll(tmp);
        });
        for (TenderBidDetailAllDTO tenderBidDetailAllDTO : result) {
            if (tenderBidDetailAllDTO.getBenchmarksRank().equals(999999)) {
                tenderBidDetailAllDTO.setBenchmarksRank(null);
            }
        }
        return result;
    }

    private List<TenderBidScoreDimension> calculatePriceScore(Set<Long> bidDetailIds) {
        final BigDecimal basePriceScore = BigDecimal.valueOf(85);
        final BigDecimal lowRate = BigDecimal.valueOf(1.5);//100%
        final BigDecimal lowScoreVal = BigDecimal.valueOf(1);
        final BigDecimal higherRate = BigDecimal.valueOf(1.5);//100%
        final BigDecimal higherScoreVal = BigDecimal.valueOf(-1);
        final BigDecimal avgRate = BigDecimal.valueOf(1);//小数，不是百分比
        //需要计算价格分
        List<TenderBidDetail> tenderBidDetails = tenderBidDetailService.listByIds(bidDetailIds);
        List<TenderBidScoreDimension> scoreDimensions = new ArrayList<>();
        //需要按照投标的项目进行分组，然后计算每个组中的价格分
        final Map<Long, List<TenderBidDetail>> collect = tenderBidDetails.stream()
                .collect(Collectors.groupingBy(TenderBidDetail::getTenderDetailId));
        for (List<TenderBidDetail> value : collect.values()) {
            final Set<TenderBidDetail> bidDetailSet = value.stream()
                    .filter(a -> Objects.equals(a.getOfferStatus(), FlagConst.Y.getCode())).collect(Collectors.toSet());
            //获取平均价

            // 使用Java 8的流API来计算平均值
            BigDecimal sum = bidDetailSet.stream()
                    .map(TenderBidDetail::getFinalCnyAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal average = sum.divide(new BigDecimal(bidDetailSet.size()), 6, BigDecimal.ROUND_HALF_UP)
                    .multiply(avgRate);
            for (TenderBidDetail tenderBidDetail : bidDetailSet) {
                final BigDecimal score = calculation(average, tenderBidDetail.getFinalCnyAmount(), basePriceScore,
                        lowRate, lowScoreVal, higherRate,
                        higherScoreVal, BigDecimal.valueOf(100));
                scoreDimensions.add(convertToBSD(tenderBidDetail.getBidId(),
                        tenderBidDetail.getId(),
                        score,
                        DimensionCode.DC_PRICE));

            }
        }
        return scoreDimensions;
    }

    @Override
    public BigDecimal calculation(
            BigDecimal avgPrice, BigDecimal offerPrice, BigDecimal baseScore, BigDecimal lowRate,
            BigDecimal lowScoreVal, BigDecimal higherRate, BigDecimal higherScoreVal,
            BigDecimal fullScore
    )
    {
        log.info(
                "计算价格分参数:基价:{},供应商报价:{},基本分:{},低比率:{},低比率加分值:{},高比率:{},高比率加分值:{},满分值:{}",
                avgPrice, offerPrice, baseScore, lowRate, lowScoreVal, higherRate, higherScoreVal,
                fullScore);
        //        return avgPrice.divide(offerPrice, 6, BigDecimal.ROUND_HALF_DOWN).multiply(fullScore);
        if (offerPrice.compareTo(avgPrice) > 0) {
            //这个是比基价高出的部分除以基价，再乘100，方便后面计算
            final BigDecimal multiply = offerPrice.subtract(avgPrice)
                    .divide(avgPrice, 6, RoundingMode.HALF_DOWN)
                    .multiply(BigDecimal.valueOf(100));
            final BigDecimal remainder = multiply.divide(higherRate, 6, RoundingMode.HALF_DOWN);
            final BigDecimal subScore = remainder.multiply(higherScoreVal);
            final BigDecimal score = baseScore.add(subScore);
            return score.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : score;
        } else if (offerPrice.compareTo(avgPrice) < 0) {
            //这个是比基价高出的部分除以基价，再乘100，方便后面计算
            final BigDecimal multiply = avgPrice.subtract(offerPrice)
                    .divide(avgPrice, 6, RoundingMode.HALF_DOWN)
                    .multiply(BigDecimal.valueOf(100));
            final BigDecimal remainder = multiply.divide(lowRate, 6, RoundingMode.HALF_DOWN);
            final BigDecimal addScore = remainder.multiply(lowScoreVal);
            final BigDecimal score = baseScore.add(addScore);
            return score.compareTo(fullScore) > 0 ? fullScore : score;
        } else {
            return baseScore;
        }
    }

    @Override
    public void stopBargaining(List<Long> id) {
        //需要把议价数据更新为已截止状态
        final List<TenderBidDetail> tenderBidDetails = tenderBidDetailService.listByIds(id);

        final Set<Long> updateIds = tenderBidDetails.stream()
                .filter(a -> a.getBargainingStatus() == BargainingStatus.BARGAINING.getValue()
                        || a.getBargainingStatus() == BargainingStatus.BARGAIN_SUCCESS.getValue())
                .map(TenderBidDetail::getId).collect(
                        Collectors.toSet());
        final Set<Long> bidIds = tenderBidDetails.stream()
                .filter(a -> a.getBargainingStatus() == BargainingStatus.BARGAINING.getValue()
                        || a.getBargainingStatus() == BargainingStatus.BARGAIN_SUCCESS.getValue())
                .map(TenderBidDetail::getBidId).collect(
                        Collectors.toSet());
        if (CollUtil.isNotEmpty(updateIds)) {
            tenderBidDetailService.update(new LambdaUpdateWrapper<TenderBidDetail>()
                    .set(TenderBidDetail::getBargainingStatus, BargainingStatus.BARGAIN_CUTOFF.getValue())
                    .in(TenderBidDetail::getId, updateIds));
        }
        if (CollUtil.isNotEmpty(bidIds)) {
            tenderBidService.update(
                    new LambdaUpdateWrapper<TenderBid>().set(TenderBid::getStatus, BidStatus.BIDDED.getStatus())
                            .in(TenderBid::getId, bidIds));

        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void calculateQuotas(SubmitBenchmarksDTO submitBenchmarksDTO) {
        final List<TenderBidDetailAllDTO> bidDetails = submitBenchmarksDTO.getBidDetails();
        //需要查询到供应商投标的数据
        final List<TenderBidDetail> tenderBidDetails = tenderBidDetailConvert.dtos2Entities(bidDetails);
        //需要先把本次招标单中的配额都清空
        tenderBidDetailService.update(new LambdaUpdateWrapper<>(TenderBidDetail.class)
                .set(TenderBidDetail::getStatus, FlagConst.N.getCode())
                .set(TenderBidDetail::getQuota, null)
                .eq(TenderBidDetail::getTenderId, submitBenchmarksDTO.getId()));
        if (CollUtil.isNotEmpty(tenderBidDetails)) {
            final Set<Long> ids = tenderBidDetails.stream().map(TenderBidDetail::getId).collect(Collectors.toSet());
            //需要更新中标
            tenderBidDetailService.update(new LambdaUpdateWrapper<>(TenderBidDetail.class)
                    .set(TenderBidDetail::getStatus, FlagConst.Y.getCode())
                    .in(TenderBidDetail::getId, ids));
            final List<TenderBidDetail> db = tenderBidDetailService.listByIds(ids).stream()
                    .filter(a -> StrUtil.isNotEmpty(a.getMaterialCode())).toList();
            if (CollUtil.isEmpty(db)) {
                return;
            }
            Tender tender = tenderService.getById(submitBenchmarksDTO.getId());
            final List<TenderBid> tenderBids = tenderBidService.list(
                    new LambdaQueryWrapper<TenderBid>().eq(TenderBid::getTenderId, tender.getId()));
            final Map<Long, String> paymentTermsCache = tenderBids.stream().collect(
                    Collectors.toMap(TenderBid::getSupplierId,
                            TenderBid::getPaymentMethod));
            List<CalculateQuotaDTO> list = Lists.newArrayList();
            for (TenderBidDetail tenderBidDetail : db) {
                final BigDecimal finalPrice = tenderBidDetail.getFinalNoTaxPrice();
                final String materialCode = tenderBidDetail.getMaterialCode();
                final Long supplierId = tenderBidDetail.getSupplierId();
                final String currency = tenderBidDetail.getCurrency();
                final CalculateQuotaDTO build = CalculateQuotaDTO.builder().currencyCode(currency)
                        .id(tenderBidDetail.getId())
                        .materialAndSupplierCode(materialCode)
                        .joinQuoteFlag(FlagConst.Y.getCode())
                        .paymentMethod(paymentTermsCache.get(supplierId))
                        .noTaxPrice(finalPrice).build();
                list.add(build);
            }
            final List<CalculateQuotaDTO> calculateQuotaDTOS = inquiryClient.calculateQuotaClient(list);
            List<TenderBidDetail> updateList = Lists.newArrayList();
            for (CalculateQuotaDTO calculateQuotaDTO : calculateQuotaDTOS) {
                TenderBidDetail tenderBidDetail = new TenderBidDetail();
                tenderBidDetail.setQuota(calculateQuotaDTO.getConfirmQuota());
                tenderBidDetail.setId(calculateQuotaDTO.getId());
                updateList.add(tenderBidDetail);
            }
            if (CollUtil.isNotEmpty(updateList)) {
                tenderBidDetailService.updateBatchById(updateList);
            }
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bidedApproval(Long tenderId, Integer type, Integer agreeFlag, String remark) {
        final Tender tender = tenderService.getById(tenderId);
        //需要推送OA
        if (type == BidedApprType.SUPERVISOR_CONFIRMATION.getCode()) {
            if (agreeFlag == FlagConst.Y.getCode()) {
                //需要判断
                //需要推送数据到OA
                //无需oa审批的采购品类
                final String noOaPurchaseCategoryCode = sysConfigClient.getTextConfig(
                        SysConfigKey.INQUIRY_FIX_NO_OA_PURCHASE_CATEGORY_CODE.getCode());
                if (noOaPurchaseCategoryCode.contains(tender.getPurchaseCategoryCode())) {
                    tender.setStatus(TenderStatus.PRICE_CONFIRMATION.getStatus());
                    tender.setUpdateTime(LocalDateTime.now());
                    tender.setUpdateId(LoginUtil.getUserId());
                    tenderService.updateById(tender);
                } else {
                    this.pushToOa(tender.getId());
                }
            } else {
                //定标界面的驳回 ，为定标驳回状态 邓京术 2025-04-18
                tender.setStatus(TenderStatus.BIDING_APPR_REJECT.getStatus());
                tender.setUpdateTime(LocalDateTime.now());
                tender.setUpdateId(LoginUtil.getUserId());
                tenderService.updateById(tender);
            }
        } else {
            if (agreeFlag == FlagConst.Y.getCode()) {
                tender.setStatus(TenderStatus.RATED.getStatus());
                tender.setUpdateTime(LocalDateTime.now());
                tender.setUpdateId(LoginUtil.getUserId());
                tenderService.updateById(tender);
            } else {
                //定标界面的驳回 ，为定标驳回状态 邓京术 2025-04-18
                tender.setStatus(TenderStatus.BIDING_APPR_REJECT.getStatus());
                tender.setUpdateTime(LocalDateTime.now());
                tender.setUpdateId(LoginUtil.getUserId());
                tenderService.updateById(tender);
            }
        }
        recordApprovalLog(tenderId, type, agreeFlag, remark);

    }

    @Override
    public PageView getApprovelList(Long id) {
        final List<TenderApprove> list = tenderApproveService.list(
                new LambdaQueryWrapper<>(TenderApprove.class).eq(TenderApprove::getTenderId, id)
                        .orderByDesc(TenderApprove::getApproveTime));
        List<TenderApproveBaseDTO> result = tenderApproveConvert.entities2DTOs(list);
        return PageUtil.getPV(result);
    }

    @Override
    public PageView getSupplierRankList(Long id) {
        final List<TenderBid> tenderBids = tenderBidService.list(
                new LambdaQueryWrapper<>(TenderBid.class).eq(TenderBid::getTenderId, id).eq(TenderBid::getAgreeFlag,1).ne(TenderBid::getStatus,3)
                        .orderByAsc(TenderBid::getRanking));
        //查询投标主表，查看这个招标单的状态
        final Tender tender = tenderService.getById(id);
        //定标主表 status = 9 已完成 status = 5 已定标 10
        if(tender.getStatus().intValue() != 9 && tender.getStatus() .intValue() != 10 && tender.getStatus() .intValue() != 5){
            //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());
            //状态10 已定标待发布时直接查排名信息 增加状态查询
            if(CollectionUtils.isNotEmpty(materialCodes) && (tender.getStatus().intValue() == 2 || tender.getStatus().intValue() == 3 || tender.getStatus().intValue() == 4 || tender.getStatus().intValue() == 98)){
                //到价格表里查找有效且时间是最近的通过价格有效日期开始和结束日期
                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);
                            //设置价格，计算公式 转换后价格 * 投标数量 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++;
                }


            }
        }


        final List<TenderBidBaseDTO> tenderBidBaseDTOS = tenderBidConvert.entities2Dtos(tenderBids);
        modelAggregationService.processRelationAggregation(tenderBidBaseDTOS);
        //定价操作时，如果没有对应价格信息时，需要提醒页面先进行价格维护，否则就不允许定价
        //这里需要考虑几个场景
        //当用户进行定价时将总价和排名一起修改，定价后的几个状态就直接查询修改后表格里的数据
        //如果时定价前的几个状态，只需要临时查询计算，不要修改表里的数据

        // Sorting the list

        tenderBidBaseDTOS.sort(Comparator.comparing(
                TenderBidBaseDTO::getRanking,
                Comparator.nullsLast(Comparator.naturalOrder())
        ));
        return PageUtil.getPV(tenderBidBaseDTOS);
    }

    //需要抽离出累加金额和计算排名的公共逻辑



    @Override
    public void releaseBenchmarks(Long id) {
        tenderService.update(new LambdaUpdateWrapper<>(Tender.class)
                .set(Tender::getStatus, TenderStatus.COMPLETED.getStatus())
                .eq(Tender::getId, id));
    }

    @Override
    public List<TenderDetailAllDTO> exportMaterial(Long tenderId) {
        final List<TenderDetail> tenderDetails = tenderDetailService.list(
                new LambdaQueryWrapper<>(TenderDetail.class).eq(TenderDetail::getTenderId, tenderId));
        List<TenderDetailAllDTO> result = tenderDetailConvert.entities2Dtos(tenderDetails);
        return result;
    }

    @Override
    public void importMaterial(MultipartFile file) throws IOException {
        List<Map<String, String>> dataList = EasyExcelImportUtils.parseExcelToData(file.getInputStream(), 2);
        List<TenderDetail> scoreExportDTOS = Lists.newArrayList();
        for (Map<String, String> stringStringMap : dataList) {
            scoreExportDTOS.add(BeanUtil.mapToBean(stringStringMap, TenderDetail.class, false));
        }
        for (TenderDetail scoreExportDTO : scoreExportDTOS) {
            final LambdaUpdateWrapper<TenderDetail> updateWrapper = new LambdaUpdateWrapper<>(TenderDetail.class)
                    .set(TenderDetail::getMaterialCode, scoreExportDTO.getMaterialCode())
                    .eq(TenderDetail::getId, scoreExportDTO.getId());
            tenderDetailService.update(updateWrapper);
            tenderBidDetailService.update(new LambdaUpdateWrapper<>(TenderBidDetail.class)
                    .set(TenderBidDetail::getMaterialCode, scoreExportDTO.getMaterialCode())
                    .eq(TenderBidDetail::getTenderDetailId, scoreExportDTO.getId()));
        }
    }

    @Override
    public List<String> supplierUnquotedMaterials(Long id) {

        final List<TenderBid> tenderBids = tenderBidService.list(
                new LambdaQueryWrapper<>(TenderBid.class).eq(TenderBid::getTenderId, id).eq(TenderBid::getAgreeFlag,1).ne(TenderBid::getStatus,3)
                        .orderByAsc(TenderBid::getRanking));
        if(CollectionUtils.isNotEmpty(tenderBids)){
            //需要排除，拒绝参与，同意参加但是不报价的供应商物料
            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);
            //把报价为空的供应商和物料全部查询出来，然后去价格信息表中进行
            //合并所有未报价的物料用于查询价格
            List<String> materialCodes = list.stream()
                    .filter(detail -> detail.getTaxPrice() == null || detail.getTaxPrice().compareTo(BigDecimal.ZERO) == 0) // 只检查null，因为BigDecimal不能等于"空"
                    .map(TenderBidDetail::getMaterialCode)
                    .collect(Collectors.toList());
            if(CollectionUtils.isNotEmpty(materialCodes)){
                //到价格表里查找有效且时间是最近的通过价格有效日期开始和结束日期
                List<PriceInfoClientDTO> effectiveMinPrice = priceInfoClient.getEffectiveMinPrice(materialCodes, new Date());
                //这时候需要重新走计算转换后未税总价，并且需要将计算总价和排名的方法重新走一遍，然后再走总分和排名排序的方法规则
                List<String> materialNumbers = effectiveMinPrice.stream()
                        .map(PriceInfoClientDTO::getMaterialCode) // 将每个 TenderBidDetail 映射到其物料编号
                        .collect(Collectors.toList()); // 收集结果到一个新列表

                //需要将没有报价且在有效价格里没有找到价格的物料拿出来，返回给前台进行先维护价格操作后才允许报价操作
                materialCodes.removeAll(materialNumbers);
                if(!materialCodes.isEmpty()){//等于空，则所有未报价物料全部查询到了价格,反之则存在没有有效价格的物料，需要返回给前台进行提示
                    return materialCodes;
                }
            }
        }

        return new ArrayList<>();
    }

    //记录审批日志
    private void recordApprovalLog(Long id, Integer type, Integer agreeFlag, String remark) {
        final TenderApprove tenderApprove = new TenderApprove();
        tenderApprove.setTenderId(id);
        tenderApprove.setApproveNode(type);
        tenderApprove.setApproveResult(agreeFlag);
        tenderApprove.setApproveTime(LocalDateTime.now());
        tenderApprove.setApproveOpinion(remark);
        tenderApprove.setApproveUserCode(LoginUtil.getAccount());
        tenderApprove.setApproveUserName(LoginUtil.getName());
        tenderApprove.setApproveUserId(LoginUtil.getUserId());
        tenderApproveService.save(tenderApprove);
    }


}
