package com.jwipc.order.logic.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jwipc.constant.DelFlag;
import com.jwipc.exception.DaoException;
import com.jwipc.order.constants.BasicPlanConst;
import com.jwipc.order.constants.CargoDataReplyConst;
import com.jwipc.order.constants.CargoPlanConst;
import com.jwipc.order.convert.BasicPlanConvert;
import com.jwipc.order.dto.BasicPlanListDTO;
import com.jwipc.order.dto.BasicPlanSapExport;
import com.jwipc.order.entity.*;
import com.jwipc.order.logic.BasicPlanLogic;
import com.jwipc.order.model.po.BasicForcaseQuotaPO;
import com.jwipc.order.model.po.BasicForecastPlanReleasePO;
import com.jwipc.order.model.po.BasicPlanPO;
import com.jwipc.order.model.po.BasicReleasePlanPO;
import com.jwipc.order.model.query.BasicForeignQuotaQuery;
import com.jwipc.order.model.query.BasicPlanQuery;
import com.jwipc.order.service.*;
import com.jwipc.order.utils.DownloadUtil;
import com.jwipc.order.utils.DownloadUtils;
import com.jwipc.order.utils.ExportExcelCommonUtil;
import com.jwipc.order.utils.PoiExcel;
import com.jwipc.page.PageUtil;
import com.jwipc.page.PageView;
import com.jwipc.security.LoginUtil;
import com.jwipc.supplier.clent.dto.SupplierDTO;
import com.jwipc.supplier.client.QuotaManagementClient;
import com.jwipc.supplier.client.SupplierClient;
import com.jwipc.tender.client.dto.QuotaManagementClientDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.baomidou.dynamic.datasource.support.DdConstants.SLAVE;


/**
 * @Author: swq
 * @Date: 2024/3/12 16:03
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class BasicPlanLogicImpl implements BasicPlanLogic {
    private final IBasicPlanService basicPlanService;
    private final IBasicForcaseDetailService detailService;
    private final IBasicForcaseQuotaService quotaService;
    private final IBasicForcaseDataService dataService;
    private final BasicPlanConvert basicPlanConvert;
    private final IPurchaseOrderDetailService purchaseOrderDetailService;
    private final IBasicForcaseOrderDataService basicForcaseOrderDataService;
    private final SupplierClient supplierClient;
    private final QuotaManagementClient quotaManagementClient;
    /**
     * 文件默认保存路径
     */
    @Value("${sys.file-path}")
    private String webUploadPath;

    @Override
    public PageView selectListByPage(BasicPlanQuery query) {
        final Page<BasicPlan> dbPage = PageUtil.toPage(query);
        List<BasicPlanPO> poList = basicPlanService.selectListByPage(dbPage, query);
        List<BasicPlanListDTO> dtoList = basicPlanConvert.POToDTOList(poList);
        return PageUtil.getPV(dtoList, dbPage.getTotal());
    }

    /**
     * 导入预测数据
     *
     * @param file      文件信息
     * @param startDate 开始时间
     * @param userId    操作人
     * @return 是否导入成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @DS(SLAVE)
    public boolean impAppraiseScore(MultipartFile file, String startDate, Long userId) {
        final LocalDate now = LocalDate.now();
        if (file == null) {
            throw new DaoException("请选择模板文件上传");
        }
        if (!file.getOriginalFilename().contains(".")) {
            throw new DaoException("文件类型正确!");
        }
        // 解析excel获取集合
        final List<Map<String, String>> list = PoiExcel.readExcel(file, file.getOriginalFilename(), true, 0, 0);
        if (list == null || list.size() == 0) {
            throw new DaoException("数据不能为空");
        }
        //保存文件到磁盘
        final String suffixName = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
        final String fileDirPath = webUploadPath + File.separator + "delivery" + File.separator;
        final String filePath = fileDirPath + new DateTime().toString("yyyyMMddHHmmss") + RandomUtil.randomNumbers(4) +
                suffixName.toLowerCase();
        if (!FileUtil.exist(fileDirPath)) {
            FileUtil.mkParentDirs(filePath);
        }
        try {
            file.transferTo(new File(filePath));
        } catch (IOException e) {
            e.printStackTrace();
            throw new DaoException("文件保存失败!");
        }
        //日期在模板中的区间
        int[] dateColBetween = {6, list.get(0).size() - 1};
        final LocalDate firstDate = LocalDate.parse(startDate);
        final long maxVersion = basicPlanService.findMaxVersion();
        final long nextVersion = maxVersion + 1;

        final BasicPlan plan = new BasicPlan();
        final String relativePath = StringUtils.replace(filePath, webUploadPath, "");
        plan.setFilePath(relativePath);

        plan.setVersion(nextVersion);
        plan.setCreateTime(LocalDateTime.now());
        plan.setCreateId(userId + StringPool.EMPTY);
        plan.setStatus(1);
        plan.setTitle(file.getOriginalFilename());
        plan.setStartTime(LocalDate.parse(startDate));
        plan.setEndTime(LocalDate.parse(startDate).plusDays(60));
        basicPlanService.save(plan);

        final Map<BasicForcaseDetail, List<BasicForcaseData>> detailMap = Maps.newHashMap();

        final Map<String, String> detailColMap = Maps.newHashMap();
        final Map<String, String> dataColMap = Maps.newHashMap();
        for (int i = 0; i < list.size(); i++) {
            // 行的列数
            int column = -1;
            try {
                final Map<String, String> item = list.get(i);
                if (i == 0) {
                    //收集模板中 标题和列下标 对应关系
                    item.keySet().forEach(k -> {
                        if (BasicPlanConst.excelCol.containsKey(item.get(k))) {
                            detailColMap.put(k, BasicPlanConst.excelCol.get(item.get(k)));
                        }
                    });
                    int weekIndex = 0;
                    for (int j = dateColBetween[0]; j < dateColBetween[1]; j++) {
                        column = j + 1;

                        if (!item.containsKey("cell" + j)) {
                            continue;
                        }

                        // 格式化日期
                        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                        dataColMap.put("cell" + j, firstDate.plusMonths(weekIndex).format(formatter));
                        weekIndex++;
                    }
                } else {
//                    if(i%2 == 0){
//                        //模板中只有奇数行有值
//                        continue;
//                    }
                    //根据 列下标和标题对应关系 写入明细数据
                    final Map<String, Object> beanMap = Maps.newHashMap();
                    beanMap.put("version", nextVersion);
                    item.keySet().forEach(k -> {
                        if (detailColMap.containsKey(k)) {
                            beanMap.put("planId", plan.getId());
                            beanMap.put(detailColMap.get(k), item.get(k));
                        }
                    });
                    final BasicForcaseDetail detailInsert = BeanUtil.mapToBean(beanMap, BasicForcaseDetail.class,
                            false);
                    BigDecimal onOrderNum = detailInsert.getOnOrderNum() ==
                            null ? BigDecimal.ZERO : detailInsert.getOnOrderNum();//在途订单量
                    detailInsert.setCreateTime(now);
                    //计划明细周期数据处理
                    for (int j = dateColBetween[0]; j < dateColBetween[1]; j++) {
                        column = j + 1;

                        final BasicForcaseData data = new BasicForcaseData();
                        BigDecimal num = StrUtil.isEmpty(item.get("cell" + j)) ? BigDecimal.ZERO : new BigDecimal(
                                item.get("cell" + j));
                        data.setNum(num);
                        data.setVersion(nextVersion);
                        //判断是日期是不是转换成数字了
                        if (NumberUtil.isNumber(dataColMap.get("cell" + j))) {
                            data.setDateTime(getTime(dataColMap.get("cell" + j)));
                        } else {
                            data.setDateTime(dataColMap.get("cell" + j));
                        }
                        data.setDateType(BasicPlanConst.dateType.month);
                        data.setDetailId(detailInsert.getId());
                        if (null != detailMap.get(detailInsert)) {
                            detailMap.get(detailInsert).add(data);
                        } else {
                            detailMap.put(detailInsert, Lists.newArrayList(data));
                        }
                    }
                }
            } catch (Exception e) {
                String message = StringUtils.isNotEmpty(
                        e.getMessage()) ? e.getMessage() : "系统无法解析单元格数据, 请检查";
                throw new DaoException("第" + (i + 1) + "行第" + column + "列出现错误！错误原因: " + message);
            }
        }

        // 去重及汇总
        this.detailHandle(detailMap);

        // 保存明细数据
        if (detailMap.keySet().size() > 0) {
            this.detailService.saveBatch(Lists.newArrayList(detailMap.keySet()));
        }

        // 日期数据塞入明细ID
        final List<BasicForcaseData> dataList = Lists.newArrayList();
        detailMap.keySet().forEach(k -> detailMap.get(k).forEach(data -> {
            data.setDetailId(k.getId());
            dataList.add(data);
        }));

        // 保存
        if (dataList.size() > 0) {
            this.dataService.saveBatch(dataList, 300);
        }
        //需要处理订单数据,还需要把po数量也处理掉
        handleOrder(detailMap.keySet(), dataList);
        return true;
    }

    private void handleOrder(Set<BasicForcaseDetail> basicForcaseDetails, List<BasicForcaseData> dataList) {
        final Map<Long, List<BasicForcaseData>> cache = dataList.stream()
                .collect(Collectors.groupingBy(BasicForcaseData::getDetailId));
        basicForcaseOrderDataService.remove(new LambdaQueryWrapper<BasicForcaseOrderData>()
                .in(BasicForcaseOrderData::getDetailId, cache.keySet()));
        List<BasicForcaseOrderData> insertLists = Lists.newArrayList();
        Map<Long, BigDecimal> totalCache = Maps.newHashMap();
        for (BasicForcaseDetail basicForcaseDetail : basicForcaseDetails) {
            final Long detailId = basicForcaseDetail.getId();
            totalCache.put(detailId, BigDecimal.ZERO);
            final List<BasicForcaseData> basicForcaseData = cache.get(detailId);
            final String productCode = basicForcaseDetail.getProductCode();
            final String factoryCode = basicForcaseDetail.getFactoryCode();
            final List<PurchaseOrderDetail> purchaseOrderDetails = purchaseOrderDetailService.list(
                    new LambdaQueryWrapper<PurchaseOrderDetail>()
                            .eq(PurchaseOrderDetail::getMaterialCode, productCode)
                            .eq(PurchaseOrderDetail::getFactoryCode, factoryCode)
                            .eq(PurchaseOrderDetail::getDelFlag, DelFlag.NORMAL.getStatus())
                            .ne(PurchaseOrderDetail::getReturnFlag, "X")
                            .gt(PurchaseOrderDetail::getNoClearNum, BigDecimal.ZERO)
                            .orderBy(true, true, PurchaseOrderDetail::getCreateTime));
            for (BasicForcaseData basicForcaseDatum : basicForcaseData) {
                Iterator<PurchaseOrderDetail> iterator = purchaseOrderDetails.iterator();
                BigDecimal num = basicForcaseDatum.getNum();
                final String dateTime = basicForcaseDatum.getDateTime();
                final Integer dateType = basicForcaseDatum.getDateType();
                while (iterator.hasNext()) {
                    BasicForcaseOrderData basicForcaseOrderData = new BasicForcaseOrderData();
                    basicForcaseOrderData.setDateTime(dateTime);
                    basicForcaseOrderData.setDateType(dateType);
                    basicForcaseOrderData.setDetailId(detailId);
                    final PurchaseOrderDetail next = iterator.next();
                    basicForcaseOrderData.setOrderCode(next.getOrderCode());
                    basicForcaseOrderData.setOrderRow(next.getOrderRow());
                    basicForcaseOrderData.setSupplierCode(next.getSupplierErpCode());
                    final BigDecimal noClearNum = next.getNoClearNum();
                    if (num.compareTo(BigDecimal.ZERO) == 0) {
                        break;
                    }
                    if (num.compareTo(new BigDecimal(noClearNum.toString())) >= 0) {
                        purchaseOrderDetails.remove(next);
                        iterator = purchaseOrderDetails.iterator();
                        basicForcaseOrderData.setNum(noClearNum);
                        num = num.subtract(noClearNum);
                        insertLists.add(basicForcaseOrderData);
                        totalCache.put(detailId, totalCache.get(detailId).add(basicForcaseOrderData.getNum()));
                    } else {
                        basicForcaseOrderData.setNum(num);
                        next.setNoClearNum(noClearNum.subtract(num));
                        insertLists.add(basicForcaseOrderData);
                        totalCache.put(detailId, totalCache.get(detailId).add(basicForcaseOrderData.getNum()));
                        break;
                    }
                }
            }
        }
        if (CollUtil.isNotEmpty(insertLists)) {
            basicForcaseOrderDataService.saveBatch(insertLists);
        }
        List<BasicForcaseData> updateList = Lists.newArrayList();
        List<BasicForcaseDetail> detailList = Lists.newArrayList();
        for (Long id : cache.keySet()) {
            final List<BasicForcaseData> basicForcaseData = cache.get(id);
            BigDecimal onOrderNum = totalCache.get(id);
            BasicForcaseDetail tmp = new BasicForcaseDetail();
            tmp.setId(id);
            tmp.setOnOrderNum(onOrderNum);
            detailList.add(tmp);
            for (BasicForcaseData data : basicForcaseData) {
                //                        //在途订单量>0
                final BigDecimal num = data.getNum();
                if (onOrderNum.compareTo(BigDecimal.ZERO) > 0) {
                    //在途订单量>发布量时，订单未清数量=发布数量
                    if (onOrderNum.compareTo(num) > 0) {
                        data.setPoNum(num);
                    }
                    if (onOrderNum.compareTo(num) == 0) {
                        data.setPoNum(num);
                    }
                    if (onOrderNum.compareTo(num) < 0) {
                        data.setPoNum(onOrderNum);
                    }
                    onOrderNum = onOrderNum.subtract(num);
                }
                updateList.add(data);
            }
        }
        if (CollUtil.isNotEmpty(updateList)) {
            dataService.updateBatchById(updateList, 300);
        }
        if (CollUtil.isNotEmpty(detailList)) {
            detailService.updateBatchById(detailList, 300);
        }
    }

    //判断 并转换时间格式 ditNumber = 43607.4166666667
    public String getTime(String ditNumber) {
        final long minTime = Timestamp.valueOf("1970-01-01 09:00:00").getTime();
        final long maxTime = Timestamp.valueOf("2038-01-19 11:00:00").getTime();
        final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        //如果不是数字
        if (!isNumeric(ditNumber)) {
            return ditNumber;
        }
        //如果是数字 小于0则 返回
        BigDecimal bd = new BigDecimal(ditNumber);
        int days = bd.intValue();//天数
        int mills = (int) Math.round(bd.subtract(new BigDecimal(days)).doubleValue() * 24 * 3600);

        //获取时间
        Calendar c = Calendar.getInstance();
        c.set(1900, Calendar.JANUARY, 1);
        c.add(Calendar.DATE, days - 2);
        int hour = mills / 3600;
        int minute = (mills - hour * 3600) / 60;
        int second = mills - hour * 3600 - minute * 60;
        c.set(Calendar.HOUR_OF_DAY, hour);
        c.set(Calendar.MINUTE, minute);
        c.set(Calendar.SECOND, second);

        Date d = c.getTime();//Date
        try {
            //时间戳区间判断
            if (minTime <= d.getTime() && d.getTime() <= maxTime) {
                return dateFormat.format(c.getTime());
            } else {
                return "outOfRange";
            }
        } catch (Exception e) {
            System.out.println("传入日期错误" + c.getTime());
            throw new DaoException("传入日期错误");
        }
    }

    //校验是否数据含小数点
    private boolean isNumeric(String str) {
        Pattern pattern = Pattern.compile("[0-9]+\\.*[0-9]*");
        Matcher isNum = pattern.matcher(str);
        return isNum.matches();
    }

    /**
     * 导入计划 - 物料去重及数量汇总
     * 根据物料去除重复的数据，汇总数量
     */
    private void detailHandle(Map<BasicForcaseDetail, List<BasicForcaseData>> detailMap) {
        final Map<String, List<BasicForcaseData>> detail = Maps.newHashMap();
        final List<BasicForcaseDetail> removeKey = Lists.newArrayList();
        detailMap.keySet().forEach(k -> {
            final Set<Object> dateList = detailMap.get(k).stream()
                    .map(t -> (t.getDateTime() + StringPool.DASH + t.getDateType())).collect(Collectors.toSet());
            if (dateList.size() < detailMap.get(k).size()) {
                throw new DaoException("日期存在重复数据");
            }
            if (null == detail.get(k.getProductCode())) {
                detail.put(k.getProductCode(), detailMap.get(k));
            } else {
                final List<BasicForcaseData> cargoData = detailMap.get(k);
                final List<BasicForcaseData> exCargoData = detail.get(k.getProductCode());
                final Map<String, BasicForcaseData> map = cargoData.stream().collect(
                        Collectors.toMap(t -> t.getDateTime() + StringPool.DASH + t.getDateType(),
                                Function.identity()));
                final Map<String, BasicForcaseData> exMap = exCargoData.stream().collect(
                        Collectors.toMap(t -> t.getDateTime() + StringPool.DASH + t.getDateType(),
                                Function.identity()));

                map.keySet().forEach(date -> {
                    if (null != exMap.get(date)) {
                        final BigDecimal exNum =
                                exMap.get(date).getNum() == null ? BigDecimal.ZERO : exMap.get(date).getNum();
                        final BigDecimal num =
                                map.get(date).getNum() == null ? BigDecimal.ZERO : map.get(date).getNum();
                        exMap.get(date).setNum(exNum.add(num));
                    }
                });
                removeKey.add(k);
            }
        });
        for (BasicForcaseDetail r : removeKey) {
            detailMap.remove(r);
        }
    }

    @Override
    public BasicPlan selectById(Long id) {
        BasicPlan basicPlan = basicPlanService.getById(id);
        return basicPlan;
    }

    @Override
    public List<BasicPlan> selectPlanList() {
        List<BasicPlan> list = basicPlanService.list(
                new LambdaQueryWrapper<BasicPlan>().ne(BasicPlan::getStatus, 1).orderBy(true, false, BasicPlan::getId));
        return list;
    }

    @Override
    public List<BasicForcaseQuota> selectQuotaList(long planId) {
        Set<Long> detailIdList = detailService.list(
                        new LambdaQueryWrapper<BasicForcaseDetail>().eq(BasicForcaseDetail::getPlanId, planId)).stream()
                .map(BasicForcaseDetail::getId).collect(Collectors.toSet());
        List<BasicForcaseQuota> quotaList = quotaService.list(
                new LambdaQueryWrapper<BasicForcaseQuota>().in(BasicForcaseQuota::getDetailId, detailIdList));
        return quotaList;
    }

    @Override
    public List<BasicForcaseData> selectDataList(long planId) {
        BasicPlan basicPlan = basicPlanService.getById(planId);
        List<BasicForcaseData> dataList = dataService.list(
                new LambdaQueryWrapper<BasicForcaseData>().eq(BasicForcaseData::getVersion, basicPlan.getVersion())
                        .groupBy(BasicForcaseData::getDateTime).orderBy(true, true, BasicForcaseData::getDateTime));
        return dataList;
    }

    @Override
    public ResponseEntity getDateData(long planId, Integer page, Integer limit, String q) {
        final BasicPlanQuery query;
        if (StrUtil.isEmpty(q)) {
            query = new BasicPlanQuery();
            query.setPlanId(planId);
        } else {
            query = JSON.parseObject(q, BasicPlanQuery.class);
            String serialNumber = query.getSerialNumber();
            if (StrUtil.isNotEmpty(serialNumber)) {
                BasicPlan basicPlan = basicPlanService.getOne(
                        new LambdaQueryWrapper<BasicPlan>().eq(BasicPlan::getSerialNumber, serialNumber));
                query.setPlanId(basicPlan.getId());
            }
        }
        if (LoginUtil.isSupplier() == true) {
            query.setSupplierId(LoginUtil.getSupplierId());
        }
        final Map<String, Object> res = Maps.newHashMap();
        final Page<BasicForcaseQuotaPO> pg = new Page<>(page, limit);
        final List<BasicForcaseQuotaPO> replyDtoList = this.quotaService.getDateDataByPlanId(pg, query, planId, page,
                limit);
        //获取中高风险物料
        //quotaService.getRiskMaterialFlag(replyDtoList);


        final List<Map<String, Object>> data = Lists.newArrayList();
        replyDtoList.forEach(r -> {
            final Map<String, Object> beanToMap = BeanUtil.beanToMap(r);
            data.add(beanToMap);
            //查找根据时间明细 已回复数据
            final List<BasicForcaseQuotaPO> quotaList = this.quotaService.getDataByDetailAndSupplier(r.getDetailId(),
                    r.getSupplierCode());
            quotaList.forEach(quota -> beanToMap.put(quota.getDateTime() + StringPool.UNDERSCORE + "reply",
                    quota.getAdjustNum()));
            final List<BasicForcaseQuotaPO> replyList = this.quotaService.getDataByDetailAndSupplier(r.getDetailId(),
                    r.getSupplierCode());
            replyList.forEach(d -> beanToMap.put(d.getDateTime(),
                    (d.getDistributeNum()) + "/" + (d.getAdjustNum() == null ? "0.00" : d.getAdjustNum())));
        });
        res.put("code", 0);
        res.put("msg", "");
        res.put("count", pg.getTotal());
        res.put("data", data);
        return ResponseEntity.ok(res);
    }

    @Override
    public ResponseEntity getSupplierReply(BasicForeignQuotaQuery query) {
        if (LoginUtil.isSupplier() == true) {
            query.setSupplier(LoginUtil.getSupplierCode());
            query.setIsRiskMaterial(null);
        } else {
            //增加采购组权限
            query.setReplyUserId(LoginUtil.getUserId());
        }

        Page<BasicForcaseQuotaPO> toPage = PageUtil.toPage(query);
        List<BasicForcaseQuotaPO> list = quotaService.findForPage(toPage, query, query.getPlanId());
        //风险物料标识  供方不可见
        if (LoginUtil.isSupplier() == true) {
            for (BasicForcaseQuotaPO dto : list) {
                dto.setIsRisk(null);
            }
        }

        toPage.setRecords(list);
        final PageView pageView = PageUtil.getPV(toPage);
        return ResponseEntity.ok(pageView);
    }

    @Override
    public ResponseEntity saveSupplierReply(String data, boolean isPurchaseReplace, String detail_remark, Long userId) {
        if (StrUtil.isEmpty(data)) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("The data is empty");
        }
        final List<BasicForcaseQuota> replyList = JSON.parseArray(data, BasicForcaseQuota.class);
        if (CollectionUtil.isEmpty(replyList)) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("The data is empty");
        }
        for (BasicForcaseQuota item : replyList) {
            item.setDetailRemark(detail_remark);
        }

        final BasicPlan plan = basicPlanService.getOne(
                new LambdaQueryWrapper<BasicPlan>().eq(BasicPlan::getVersion, replyList.get(0).getVersion()));
        final boolean success = this.saveSupplierReply(plan, replyList, isPurchaseReplace, userId);
        return ResponseEntity.ok(success);
    }

    @Override
    public ResponseEntity byDetailIdGetPlanData(Long detailId, String supplierCode) {
        final Map<String, Object> res = Maps.newHashMap();
        final List<BasicForcaseQuotaPO> dataList = dataService.getPlanDetailData(detailId, supplierCode);
        res.put("rows", dataList);
        res.put("total", dataList.size());

        if (dataList.size() > 0) {
            final Long version = dataList.get(0).getVersion();
            final Long previousVersion = basicPlanService.findPreviousVersion(version);
            final Map<String, BasicForcaseQuotaPO> perDataMap = quotaService.findReplyListByVersionId(previousVersion)
                    .stream().collect(Collectors.toMap(
                            t -> (t.getDateTime() + StringPool.UNDERSCORE + t.getSupplierCode() +
                                    StringPool.UNDERSCORE + t.getProductCode()), Function.identity()));
            res.put("perData", perDataMap);
        }

        return ResponseEntity.ok(res);
    }

    @Override
    public void getReplyExport(HttpServletRequest request, HttpServletResponse response, Long planId,
                               Integer status, String isRiskMaterial, String supplier, String purchaseGroup)
    {
        final String supplierCode;
        //内部用户 限定采购组
        Long replyUserId = null;
        //判断是否为供应商
        if (LoginUtil.isSupplier() == true) {
            SupplierDTO suppl = supplierClient.getSupplierById(LoginUtil.getSupplierId());
            supplierCode = suppl.getSupplierCode();
            isRiskMaterial = null;
        } else {
            supplierCode = supplier;
            replyUserId = LoginUtil.getUserId();
        }
        String fileName = "预测回复导出" + StringPool.UNDERSCORE + DateTime.now().toString("yyyyMMddHHmmss");
        XSSFWorkbook workbook = null;
        try {
            workbook = this.bySupplierCodeExportReply(request, response, planId,
                    supplierCode, status, replyUserId, isRiskMaterial, purchaseGroup);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        try {
            DownloadUtil.downExcelXlsx(response, request, fileName, workbook);
        } catch (IOException e) {
            e.printStackTrace();
            throw new DaoException("导出出现错误");
        }

    }

    @Override
    public ResponseEntity replyImport(@RequestPart("file") MultipartFile file, Long planId) {
        //判断是否为供应商
        String supplierCode = "";
        Long userId = null;
        if (LoginUtil.isSupplier() == true) {
            SupplierDTO suppl = supplierClient.getSupplierById(LoginUtil.getSupplierId());
            supplierCode = suppl.getSupplierCode();
            //return ResponseEntity.badRequest().body("您不是供应商无法导入回复");
        } else {
            userId = LoginUtil.getUserId();
        }

        final boolean success = this.replySuppImport(file, planId, supplierCode, userId);
        return ResponseEntity.ok(success);
    }

    @Override
    public ResponseEntity checkPlanReleaseReplySituation(Long planId, Integer checkType) {
        if (1 == checkType) {
            BasicPlanQuery query = new BasicPlanQuery();
            query.setUserId(LoginUtil.getUserId());
            query.setPlanId(planId);

            final List<BasicForecastPlanReleasePO> list = basicPlanService.findWaitReleaseData(null, query);
//            if (CollectionUtils.isEmpty(list)) {
//                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
//                        .body(this.getError("选中的计划没有物料需要发布了"));
//            }
            return ResponseEntity.ok(StringPool.EMPTY);
        }
        if (2 == checkType) {
            BasicForeignQuotaQuery query = new BasicForeignQuotaQuery();
            query.setUserId(LoginUtil.getUserId());

            final List<BasicForcaseQuotaPO> list = quotaService.findForPage(null, query, planId);
//            if (CollectionUtils.isEmpty(list)) {
//                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
//                        .body(this.getError("选中的计划没有物料需要确认了"));
//            }
            return ResponseEntity.ok(StringPool.EMPTY);
        }
        return ResponseEntity.ok(StringPool.EMPTY);
    }

    @Override
    public ResponseEntity getConfirmData(
            Boolean isBatch, Long detailId, String supplierCode, String replyIds, String refusalReason,
            String confirmType
    )
    {

        final boolean success = this.confirmReplyData(isBatch, detailId, supplierCode, replyIds,
                refusalReason, confirmType, LoginUtil.getUserId());
        return ResponseEntity.ok(success);
    }

    @Override
    public BasicForecastPlanReleasePO findPlanSituation(BasicPlanQuery query) {
        BasicForecastPlanReleasePO planSituation = basicPlanService.findPlanSituation(query);
        return planSituation;
    }

    @Override
    public ResponseEntity findWaitReleaseData(Integer page, Integer rows, BasicPlanQuery query) {
        query.setUserId(LoginUtil.getUserId());
        Page<BasicForecastPlanReleasePO> toPage = new Page(page, rows);
        List<BasicForecastPlanReleasePO> list = basicPlanService.findWaitReleaseData(toPage, query);
        toPage.setRecords(list);
        //需要获取moq进行moq计算
        //需要查询供应商的moq和mpq
        Map<String, List<QuotaManagementClientDTO>> quotaCache = Maps.newHashMap();
        String keyFormat = "{}@@{}";
        for (BasicForecastPlanReleasePO po : list) {
            final String productCode = po.getProductCode();
            final String factoryCode = po.getFactoryCode();
            final String key = StrUtil.format(keyFormat, productCode, factoryCode);
            final List<QuotaManagementClientDTO> quotaManagementClientDTOS = quotaCache.get(
                    key);
            if (Objects.isNull(quotaManagementClientDTOS)) {
                final List<QuotaManagementClientDTO> byMaterialCodeAndFactoryCode =
                        quotaManagementClient.getByMaterialCodeAndFactoryCode(
                                productCode, factoryCode);
                quotaCache.put(key, byMaterialCodeAndFactoryCode);
            }
        }

        //分组
        final Map<String, List<BasicForecastPlanReleasePO>> collect = list.stream()
                .filter(a -> StrUtil.isNotEmpty(a.getSupplierCode()))
                .collect(Collectors.groupingBy(a -> StrUtil.format(keyFormat, a.getProductCode(), a.getFactoryCode())));
        //需要对数据进行moq处理
        for (String s : collect.keySet()) {
            final List<BasicForecastPlanReleasePO> basicForecastPlanReleasePOS = collect.get(s);
            //需要先统计总分配数量，然后按照moq进行重新分配
            final BigDecimal totalNum = basicForecastPlanReleasePOS.stream()
                    .map(BasicForecastPlanReleasePO::getAdjustmentNum)
                    .filter(num -> num != null) // Ensure no null values
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            final List<QuotaManagementClientDTO> quotaManagementClientDTOS = quotaCache.get(s);
            final Stream<QuotaManagementClientDTO> sorted = quotaManagementClientDTOS.stream()
                    .sorted(Comparator.comparing(QuotaManagementClientDTO::getQuota).reversed());
            //现在需要根据顺序分配数据
            // TODO: 2024/6/3 需要处理moq jiez
        }
        return ResponseEntity.ok(PageUtil.getPV(toPage));
    }


    @Override
    public String downloadPlanFileByTypeAndPlanId(Long planId,
                                                  @RequestParam(value = "type", defaultValue = "") String type,
                                                  HttpServletRequest request,
                                                  HttpServletResponse response,
                                                  @RequestParam(value = "downFileName", defaultValue = "") String downFileName,
                                                  @RequestParam(value = "downFilePath", defaultValue = "") String downFilePath)
    {
        final String filePath;
        final String fileName;
        switch (type) {
            //标准预测
            case "basic":
                final BasicPlan basicPlan = basicPlanService.getById(planId);
                filePath = basicPlan.getFilePath();
                fileName = basicPlan.getTitle();
                break;
            default:
                if (StrUtil.isNotEmpty(downFilePath)) {
                    filePath = downFilePath;
                    fileName = StrUtil.isEmpty(downFileName) ? new File(filePath).getName() : downFileName;
                } else {
                    throw new RuntimeException("File does not exist!");
                }
        }
        final String finalFilePath = webUploadPath + filePath;
        if (StrUtil.isEmpty(finalFilePath)) {
            throw new RuntimeException("File does not exist!");
        }
        final File file = new File(finalFilePath);
        if (!file.exists()) {
            throw new RuntimeException("File does not exist!");
        }
        try {
            DownloadUtils.downloadFile(fileName, finalFilePath, request, response);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("File download failed!");
        }
        return null;
    }

    @Override
    public ResponseEntity saveReleaseData(Long planId, String data) {
        if (StrUtil.isEmpty(data)) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("参数为空");
        }
        List<BasicReleasePlanPO> replyList = JSON.parseArray(data, BasicReleasePlanPO.class);
        if (CollectionUtil.isEmpty(replyList)) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("参数为空");
        }
        final BasicPlan plan = basicPlanService.getById(planId);
        if (!this.saveReleaseReply(plan, replyList)) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("保存发布数据失败, 请联系管理员");
        }
        final Set<Long> detailIds = replyList.stream().map(BasicReleasePlanPO::getDetailId)
                .collect(Collectors.toSet());
        if (CollUtil.isNotEmpty(detailIds)) {
            basicForcaseOrderDataService.remove(
                    new LambdaQueryWrapper<BasicForcaseOrderData>().in(BasicForcaseOrderData::getDetailId, detailIds));
        }
        return ResponseEntity.ok(StringPool.EMPTY);
    }

    @Override
    public void downLoadPcn(HttpServletRequest request,
                            HttpServletResponse response,
                            @RequestParam(value = "planId") Long planId,
                            @RequestParam(value = "purchase_group") String purchase_group
    )
    {
        try {

            BasicPlanQuery query = new BasicPlanQuery();
            query.setUserId(LoginUtil.getUserId());
            query.setPurchaseGroup(purchase_group);
            query.setPlanId(planId);

            List<BasicForecastPlanReleasePO> list = basicPlanService.findWaitReleaseData(null, query);

            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

            List<Map<String, String>> listMap = Lists.newArrayList();
            for (BasicForecastPlanReleasePO vo : list) {
                Map<String, String> map = Maps.newHashMap();
                map.put("物料", vo.getProductCode());
                map.put("物料名称", vo.getProductName());
                map.put("采购组", vo.getPurchaseGroup());
                map.put("预测数量", vo.getPlanNum() == null ? "" : vo.getPlanNum().toString());
                map.put("未清总数", vo.getOnOrderNum() == null ? "" : vo.getOnOrderNum().toString());
                map.put("工厂", vo.getFactoryCode());
                map.put("供应商", vo.getSupplierCode());
                map.put("供应商名称", vo.getSupplierName());
                map.put("未清PO数量", vo.getOutstandingPoNum() == null ? "" : vo.getOutstandingPoNum().toString());
                map.put("配额（%）", vo.getOriQuota() == null ? "" : vo.getOriQuota().toString());
                //map.put("分配数量", vo.getDistributeNum()==null?"":vo.getDistributeNum().toString());
                map.put("调整配额（%）", vo.getAdjustmentQuota() == null ? "" : vo.getAdjustmentQuota().toString());
                //map.put("调整数量", vo.getAdjustmentNum()==null?"":vo.getAdjustmentNum().toString());
                //map.put("调整原因", vo.getAdjustmentReason());
                //map.put("备注", vo.getRemark());
                //if(vo.getOriQuota()==null){listMap.add(map);}
                listMap.add(map);
            }

            //String[] titleAndKey = new String[]{"物料", "物料名称", "采购组", "是否风险物料", "预测数量", "供应商", "供应商名称", "配额（%）",
            // "分配数量", "调整配额（%）", "调整数量", "调整原因", "备注"};
            //Integer[] cellLength = new Integer[]{12, 12, 12, 18, 12, 12, 32, 12, 24, 18, 12, 20, 20};

            String[] titleAndKey = new String[]{"物料", "物料名称", "采购组", "预测数量", "未清总数", "工厂", "供应商", "供应商名称", "未清PO数量",
                    "配额（%）", "调整配额（%）"};
            Integer[] cellLength = new Integer[]{12, 12, 12, 12, 12, 12, 12, 32, 12, 12, 18};
            String[] cellNumberFormat = new String[]{};

            String fileName = "发布数据导出" + DateUtil.format(new Date(), "yyyyMMddHHmmssSSS");
            ExportExcelCommonUtil.setExcelResponseInfo(request, response, fileName);
            ExportExcelCommonUtil.exportExcel2(listMap, titleAndKey, titleAndKey, cellNumberFormat, cellLength,
                    response.getOutputStream());


        } catch (Exception e) {
            e.printStackTrace();
            log.error("发布数据导出异常: {}", e.getMessage(), e);
        }
    }

    @Override
    public void downLoadRisk(HttpServletRequest request,
                             HttpServletResponse response, String product, String supplier, String isRisk,
                             String serialNumber)
    {
        try {
            BasicPlanQuery query = new BasicPlanQuery();
            query.setSupplier(supplier);
            query.setIsRisk(isRisk);
            query.setProduct(product);
            if (StrUtil.isNotEmpty(serialNumber)) {
                BasicPlan basicPlan = basicPlanService.getOne(
                        new LambdaQueryWrapper<BasicPlan>().eq(BasicPlan::getSerialNumber, serialNumber));
                query.setPlanId(basicPlan.getId());
            }

            if (LoginUtil.isSupplier() == true) {
                query.setSupplierId(LoginUtil.getSupplierId());
            }

            final Map<String, Object> res = Maps.newHashMap();
            final Page<BasicForcaseQuotaPO> pg = new Page<>(1, 1000);
            final List<BasicForcaseQuotaPO> replyDtoList = this.quotaService.getDateDataByPlanId(pg, query,
                    query.getPlanId(), 1, 1000);

            final List<Map<String, String>> listMap = Lists.newArrayList();
            replyDtoList.forEach(r ->
            {
                Map<String, String> map = Maps.newHashMap();
                map.put("物料编码", r.getProductCode());
                map.put("物料名称", r.getProductName());
                map.put("工厂编码", r.getFactoryCode());
                String risk = r.getIsRisk();
                if (("1").equals(risk)) {
                    risk = "是";
                } else {
                    risk = "否";
                }
                map.put("是否预警", risk);
                map.put("供应商编码", r.getSupplierCode());
                map.put("供应商名称", r.getSupplierName());
                //查找根据时间明细 已回复数据
                final List<BasicForcaseQuotaPO> replyList = this.quotaService.getDataByDetailAndSupplier(
                        r.getDetailId(), r.getSupplierCode());
                replyList.forEach(d ->
                {
                    int index = replyList.indexOf(d);
                    String colTitle = "M" + (index + 1);
                    map.put(colTitle + "发布量",
                            d.getDistributeNum().toString());
                    map.put(colTitle + "回复量",
                            d.getAdjustNum() == null ? BigDecimal.ZERO.toString() : d.getAdjustNum().toString());
                    map.put(colTitle + "差异量",
                            (d.getAdjustNum() == null ? BigDecimal.ZERO : d.getAdjustNum()).subtract(
                                    d.getDistributeNum()) + "");
                });
                listMap.add(map);

            });
            List<String> titlList = new ArrayList<>();
            titlList.add("物料编码");
            titlList.add("物料名称");
            titlList.add("工厂编码");
            titlList.add("是否预警");
            titlList.add("供应商编码");
            titlList.add("供应商名称");
            List<Integer> cellList = new ArrayList<>();
            cellList.add(12);
            cellList.add(30);
            cellList.add(12);
            cellList.add(12);
            cellList.add(12);
            cellList.add(30);
            final List<BasicForcaseQuotaPO> replyList1 = this.quotaService.getDataByDetailAndSupplier(
                    replyDtoList.get(0).getDetailId(), replyDtoList.get(0).getSupplierCode());
            replyList1.forEach(d ->
            {
                int index = replyList1.indexOf(d);
                String colTitle = "M" + (index + 1);
                titlList.add(colTitle + "发布量");
                cellList.add(20);
                titlList.add(colTitle + "回复量");
                cellList.add(20);
                titlList.add(colTitle + "差异量");
                cellList.add(20);
            });
            String[] titleAndKey = titlList.toArray(new String[titlList.size()]);
            Integer[] cellLength = cellList.toArray(new Integer[cellList.size()]);
/*
            String[] titleAndKey = new String[]{"物料编码", "物料名称", "工厂编码", "是否预警", "供应商编码", "供应商名称", "M1发布量/回复量",
                    "M2" +
                            "发布量/回复量", "M3发布量/回复量", "M4发布量/回复量", "M5发布量/回复量", "M6发布量/回复量", "M7发布量/回复量", "M8发布量/回复量",
                    "M9" +
                            "发布量/回复量", "M10发布量/回复量", "M11发布量/回复量", "M12发布量/回复量", "M13发布量/回复量",};
            Integer[] cellLength = new Integer[]{12, 30, 12, 10, 12, 30, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
                    12, 12};*/
            String[] cellNumberFormat = new String[]{};

            String fileName = "预警数据导出" + DateUtil.format(new Date(), "yyyyMMddHHmmssSSS");
            ExportExcelCommonUtil.setExcelResponseInfo(request, response, fileName);
            ExportExcelCommonUtil.exportExcel2(listMap, titleAndKey, titleAndKey, cellNumberFormat, cellLength,
                    response.getOutputStream());


        } catch (Exception e) {
            e.printStackTrace();
            log.error("预警数据导出异常: {}", e.getMessage(), e);
        }
    }


    @Override
    public Map<String, Object> impXunJiaMaterial(@RequestPart("file") MultipartFile file) {
        Map<String, Object> map = new HashMap<>();

        //所有数据集合
        List<Map<String, Object>> excelList = new ArrayList<>();

        if (!file.getOriginalFilename().contains(".")) {
            map.put("code", "500");
            map.put("message", "文件类型不正确");
            map.put("data", null);
            return map;
        }

        // 解析Excel获取集合
        final List<Map<String, String>> list = PoiExcel.readExcel(file, file.getOriginalFilename(), true, 0, 0);

        if (CollectionUtils.isEmpty(list)) {
            map.put("code", "500");
            map.put("message", "数据不能为空");
            map.put("data", null);
            return map;
        }
        //行数大于1则代表有数据，第一行为标题
        if (list.size() > 1) {
            //循环行
            for (int row = 1; row < list.size(); row++) {

                Map<String, Object> excelRowMap = new HashMap<>();

                String wlh = list.get(row).get("cell0");//物料号
                String wlmc = list.get(row).get("cell1");//物料名称
                String cgz = list.get(row).get("cell2");//采购组
                String ycsl = list.get(row).get("cell3");//预测数量
                String wqzs = list.get(row).get("cell4");//未清总数
                String gc = list.get(row).get("cell5");//工厂
                String gys = list.get(row).get("cell6");//供应商
                String gysmc = list.get(row).get("cell7");//供应商名称
                String wqposl = list.get(row).get("cell8");//未清PO数量
                String pe = list.get(row).get("cell9") == null ? "" : list.get(row).get("cell9");//配额（%）
                //String fpsl=list.get(row).get("cell8");//分配数量
                String tzpe = list.get(row).get("cell10") == null ? "" : list.get(row).get("cell10");//调整配额（%）
                //String tzsl=list.get(row).get("cell10");//调整数量
                //String tzyy=list.get(row).get("cell11");//调整原因
                //String bz=list.get(row).get("cell12");//备注

                excelRowMap.put("wlh", wlh);//物料号
                excelRowMap.put("wlmc", wlmc);//物料名称
                excelRowMap.put("cgz", cgz);//采购组
                excelRowMap.put("ycsl", ycsl);//预测数量
                excelRowMap.put("wqzs", wqzs);//未清总数
                excelRowMap.put("gc", gc);//工厂
                excelRowMap.put("gys", gys);//供应商
                excelRowMap.put("gysmc", gysmc);//供应商名称
                excelRowMap.put("wqposl", wqposl);//供应商名称
                excelRowMap.put("pe", pe);//配额（%）
                //excelRowMap.put("fpsl",fpsl);//分配数量
                excelRowMap.put("tzpe", tzpe);//调整配额（%）
                //excelRowMap.put("tzsl",tzsl);//调整数量
                //excelRowMap.put("tzyy",tzyy);//调整原因
                //excelRowMap.put("bz",bz);//备注

                excelList.add(excelRowMap);

                basicPlanService.updateQuota(wlh, gys, gc, pe, tzpe);
            }
        } else {
            map.put("code", "500");
            map.put("message", "数据不能为空");
            map.put("data", null);
            return map;
        }
        map.put("code", "200");
        map.put("message", "导入数据获取成功！");
        map.put("data", excelList);
        return map;
    }

    private JSONObject getError(String message) {
        JSONObject error = new JSONObject();
        error.put("status", 500);
        error.put("message", message);
        return error;
    }

    @Override
    public ResponseEntity saveSupplierReplys(String data, boolean isPurchaseReplace) {
        Boolean success = true;
        final List<BasicForcaseQuota> replyList = JSON.parseArray(data, BasicForcaseQuota.class);
        if (CollectionUtil.isEmpty(replyList)) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("The data is empty");
        }

        for (int xb = 0; xb < replyList.size(); xb++) {

            List<BasicForcaseQuota> bfqList = new ArrayList<>();

            Long detailId = replyList.get(xb).getDetailId();
            String supplierCode = replyList.get(xb).getSupplierCode();

            final List<BasicForcaseQuotaPO> dataList = this.dataService.getPlanDetailData(detailId, supplierCode);

            for (int xb2 = 0; xb2 < dataList.size(); xb2++) {
                BasicForcaseQuota bfq = new BasicForcaseQuota();
                bfq.setId(dataList.get(xb2).getId());
                bfq.setDetailId(dataList.get(xb2).getDetailId());
                bfq.setDataId(dataList.get(xb2).getDataId());
                bfq.setProductCode(dataList.get(xb2).getProductCode());
                bfq.setProductName(dataList.get(xb2).getProductName());
                bfq.setSupplierCode(dataList.get(xb2).getSupplierCode());
                bfq.setSupplierName(dataList.get(xb2).getSupplierName());
                bfq.setQuotaNum(dataList.get(xb2).getQuotaNum());
                bfq.setAdjustNum(dataList.get(xb2).getAdjustNum());
                bfq.setAdjustDate(dataList.get(xb2).getAdjustDate());
                bfq.setDistributeNum(dataList.get(xb2).getDistributeNum());
                bfq.setAdjustReason(dataList.get(xb2).getAdjustReason());
                bfq.setStatus(dataList.get(xb2).getStatus());
                bfq.setRefusalReason(dataList.get(xb2).getRefusalReason());
                bfq.setRemark(dataList.get(xb2).getRemark());
                bfq.setVersion(dataList.get(xb2).getVersion());

                bfqList.add(bfq);
            }

            final BasicPlan plan = basicPlanService.getOne(
                    new LambdaQueryWrapper<BasicPlan>().eq(BasicPlan::getVersion, bfqList.get(0).getVersion()));

            final boolean success2 = this.saveSupplierReply(plan, bfqList, isPurchaseReplace,
                    LoginUtil.getUserId());
            if (success2 != true) {
                success = success2;
            }
        }
        return ResponseEntity.ok(success);

    }

    @Override
    public ResponseEntity getSupplierReply(Integer page, Integer rows, Long planId) {
        Page<BasicForcaseQuotaPO> toPage = new Page(page, rows);
        List<BasicForcaseQuotaPO> list = quotaService.getSupplierReply(planId);
        toPage.setRecords(list);
        return ResponseEntity.ok(PageUtil.getPV(toPage));
    }

    /**
     * 保存发布信息
     *
     * @param replyList 发布信息
     * @return 是否保存成功
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean saveReleaseReply(BasicPlan plan, List<BasicReleasePlanPO> replyList) {
        // 独供物料的供应商配额如果没有填写时默认100
        final Map<Long, List<BasicReleasePlanPO>> prMap = replyList.stream()
                .collect(Collectors.groupingBy(BasicReleasePlanPO::getDetailId));
        for (Map.Entry<Long, List<BasicReleasePlanPO>> in : prMap.entrySet()) {
            List<BasicReleasePlanPO> prList = in.getValue();
            if (CollectionUtils.isNotEmpty(prList) && prList.size() == 1) {
                BasicReleasePlanPO pr = prList.get(0);
                if (null == pr.getQuota()) {
                    pr.setQuota(new BigDecimal("100"));
                    pr.setDistributeNum(pr.getPlanNum());
                }
            }
        }

        // 检查配额 1.配额不能超过100; 2.配额不能小于等于0
        final BigDecimal exceedQuota = BigDecimal.TEN.multiply(BigDecimal.TEN);
        for (BasicReleasePlanPO release : replyList) {
            // 配额
            final BigDecimal quota = null == release.getQuota() ? (
                    release.getApplyNum() == null ? BigDecimal.ZERO : release.getApplyNum()) : release.getQuota();
            if (null == quota) {
                throw new DaoException("计划数据(物料号 - 供应商编码): " + release.getProductCode() + " - " +
                        release.getSupplierCode() + "的配额填写不正确");
            }

            if (quota.compareTo(exceedQuota) > 0) {
                throw new DaoException("计划数据(物料号 - 供应商编码): " + release.getProductCode() + " - " +
                        release.getSupplierCode() + "的配额填写不正确, 配额大于100%");
            }

            if (quota.compareTo(BigDecimal.ZERO) <= 0) {
                throw new DaoException("计划数据(物料号 - 供应商编码): " + release.getProductCode() + " - " +
                        release.getSupplierCode() + "的配额填写不正确, 配额小于等于0");
            }
        }

        // Map<detailId, List<供应商对应物料明细>>
        final Map<Long, List<BasicReleasePlanPO>> releaseDtoMap = replyList.stream().collect(
                Collectors.toMap(BasicReleasePlanPO::getDetailId, Lists::newArrayList, (t1, t2) -> {
                    t1.addAll(t2);
                    return t1;
                }));

        // 计划明细周期数据
        final List<BasicForcaseData> dataList = this.dataService.list(
                new LambdaQueryWrapper<BasicForcaseData>().in(BasicForcaseData::getDetailId, releaseDtoMap.keySet()));

        final List<BasicForcaseQuota> insertList = Lists.newArrayList();
        dataList.forEach(d -> {
            final List<BasicReleasePlanPO> releaseDtoList = releaseDtoMap.get(d.getDetailId());
            releaseDtoList.forEach(releaseDto -> {
                final BasicForcaseQuota reply = new BasicForcaseQuota();
                reply.setDetailId(d.getDetailId());
                reply.setDataId(d.getId());
                reply.setSupplierCode(releaseDto.getSupplierCode());
                reply.setSupplierName(releaseDto.getSupplierName());
                reply.setProductCode(releaseDto.getProductCode());
                reply.setProductName(releaseDto.getProductName());
                // 配额
                final BigDecimal quota = releaseDto.getQuota() == null ? (releaseDto.getApplyNum() ==
                        null ? BigDecimal.ZERO : releaseDto.getApplyNum()) : releaseDto.getQuota();
                reply.setQuotaNum(quota.toString());
                //需要统计未清
                final List<BasicForcaseOrderData> orderData = basicForcaseOrderDataService.list(
                        new LambdaQueryWrapper<BasicForcaseOrderData>().eq(BasicForcaseOrderData::getDetailId,
                                        d.getDetailId())
                                .eq(BasicForcaseOrderData::getDateType, d.getDateType())
                                .eq(BasicForcaseOrderData::getSupplierCode, releaseDto.getSupplierCode())
                                .eq(BasicForcaseOrderData::getDateTime, d.getDateTime()));
                final BigDecimal count = orderData.stream().map(BasicForcaseOrderData::getNum)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                final BigDecimal distributeNum = (
                        null == d.getNum() ? BigDecimal.ZERO : d.getNum().subtract(d.getPoNum())).multiply(quota)
                        .multiply(BigDecimal.valueOf(0.01));
                reply.setDistributeNum(distributeNum.add(count));
                reply.setVersion(d.getVersion());
                reply.setStatus(CargoDataReplyConst.Status.EFFECTIVE);
                reply.setAdjustReason(releaseDto.getAdjustmentReason());
                reply.setRemark(releaseDto.getRemark());
                insertList.add(reply);
            });
        });

        //发布新数据之前作废之前的数据
        this.basicPlanService.invalidAllPlanData();

        final LocalDate now = LocalDate.now();
        // 更新计划状态 并且 根据上个计划版本号 生成新的版本号
        final String previousSerialNumber = basicPlanService.findNewSerialNumber(CargoPlanConst.tableNameList.basicPlan,
                plan.getId(), null);
        plan.setSerialNumber(genSerialNumber(DateTime.now(), previousSerialNumber));
        plan.setStatus(CargoPlanConst.planStatus.has_release);
        plan.setReleaseDate(now);
        basicPlanService.updateById(plan);

        return quotaService.saveBatch(insertList, 300);
    }

    /**
     * 根据当前生成新的版本号
     *
     * @param now          当前时间
     * @param serialNumber 当前版本号
     * @return 生成的版本号
     */
    public String genSerialNumber(DateTime now, String serialNumber) {
        if (StrUtil.isEmpty(serialNumber)) {
            // 本月第一个
            return now.toString("yyyyMM") + CargoPlanConst.serial.list.get(0);
        } else {
            if (serialNumber.length() == 7) {
                // 获取版本号尾号对应的索引
                final String ser = serialNumber.substring(6);
                int indexOf = CargoPlanConst.serial.list.indexOf(ser);

                // 获取本次生成的版本号与历史版本号的月数差值
                final String dateStr = serialNumber.substring(0, 6);

                int nowMonth = DateUtil.month(now.toJdkDate()) + 1;
                int snMonth = DateUtil.month(DateUtil.parse(dateStr, "yyyyMM")) + 1;

                if (indexOf >= 0) {
                    if (indexOf == 55) {
                        throw new DaoException("版本号已到上限");
                    } else {
                        if (nowMonth > snMonth) {
                            return now.toString("yyyyMM") + CargoPlanConst.serial.list.get(0);
                        }
                        return now.toString("yyyyMM") + CargoPlanConst.serial.list.get(indexOf + 1);
                    }
                } else {
                    throw new DaoException("生成版本号出现错误");
                }
            }
        }
        throw new DaoException("生成版本号出现错误");
    }

    /**
     * 保存供应商回复数据
     *
     * @param replyList 回复数据
     * @return 保存是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean saveSupplierReply(BasicPlan plan, List<BasicForcaseQuota> replyList, boolean isPurchaseReplace,
                                     Long userId)
    {
        LocalDateTime now = LocalDateTime.now();

        final Set<String> supplierCodeList = new HashSet<>();
        final Set<Long> detailIdList = new HashSet<>();
        final Set<Long> versionList = new HashSet<>();
        final List<BasicForcaseQuota> updateQuota = Lists.newArrayList();
        for (BasicForcaseQuota i : replyList) {
            supplierCodeList.add(i.getSupplierCode());
            detailIdList.add(i.getDetailId());
            versionList.add(i.getVersion());
            final BasicForcaseQuota update = new BasicForcaseQuota();
            update.setId(i.getId());
            update.setAdjustNum(i.getAdjustNum());
            update.setAdjustDate(now);
            update.setStatus(BasicPlanConst.QuotaStatus.PENDING_REPLY);//已回复
            update.setDetailRemark(i.getDetailRemark());
            updateQuota.add(update);
        }
        if (supplierCodeList.size() < 1 || detailIdList.size() < 1 || versionList.size() < 1) {
            throw new DaoException("数据不正确,保存失败");
        }
        final BasicForcaseQuota reply = quotaService.getOne(new LambdaQueryWrapper<BasicForcaseQuota>()
                .eq(BasicForcaseQuota::getSupplierCode, supplierCodeList.iterator().next())
                .eq(BasicForcaseQuota::getDetailId, detailIdList.iterator().next()).last(("LIMIT 1")));
        if (null == reply) {
            throw new DaoException("供应商回复数据为空");
        }
        //更新计划状态
        plan.setStatus(CargoPlanConst.planStatus.has_reply);//已回复
        basicPlanService.updateById(plan);
        //明细表更新是否预警状态
        for (Long detailId : detailIdList) {
            List<BasicForcaseQuota> quotaList = replyList.stream().filter(t -> t.getDetailId().equals(detailId))
                    .collect(Collectors.toList());
            if (quotaList != null && quotaList.size() > 0) {
                //分配总数
                BigDecimal fpNum = new BigDecimal("0.00");
                //回复总数
                BigDecimal hfNum = new BigDecimal("0.00");
                for (BasicForcaseQuota forcaseQuota : quotaList) {
                    fpNum = fpNum.add(forcaseQuota.getDistributeNum());
                    hfNum = hfNum.add(forcaseQuota.getAdjustNum());
                }
                //分配总数>回复总数 时 “预警状态”该成是
                if (fpNum.compareTo(hfNum) > 0) {
                    BasicForcaseDetail detail = new BasicForcaseDetail();
                    detail.setId(detailId);
                    detail.setIsRisk("1");
                    detailService.updateById(detail);
                }

            }
        }
        quotaService.updateBatchById(updateQuota);

//        this.baseMapper.updateQuotaAdjust(updateQuota, date, BasicPlanConst.QuotaStatus.PENDING_REPLY);
        return true;
    }


    /**
     * 确认供应商的回复数据
     *
     * @param isBatch       是否批量操作
     * @param detailId      明细ID
     * @param supplierCode  供应商编码
     * @param replyIds      回复ids
     * @param refusalReason 拒绝原因
     * @return 是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean confirmReplyData(Boolean isBatch, Long detailId, String supplierCode, String replyIds,
                                    String refusalReason, String confirmType, Long userId)
    {
        final Date date = new Date();
        final int status;
        if (StrUtil.equals("refuse", confirmType)) {
            if (StrUtil.isEmpty(refusalReason)) {
                throw new DaoException("拒绝原因为空");
            }
            status = BasicPlanConst.QuotaStatus.PURCHASE_REFUSE;
        } else {
            status = BasicPlanConst.QuotaStatus.PURCHASE_CONFIRM;
        }
        if (isBatch != null && isBatch) {
            // 批量操作
            if (detailId == null || StrUtil.isEmpty(supplierCode)) {
                throw new DaoException("参数有误");
            }
            final List<BasicForcaseQuota> updateList = quotaService.list(
                    new LambdaQueryWrapper<BasicForcaseQuota>().eq(BasicForcaseQuota::getDetailId, detailId)
                            .eq(BasicForcaseQuota::getSupplierCode, supplierCode));
            updateList.forEach(i -> {
                i.setStatus(status);
                i.setRefusalReason(refusalReason);
                i.setConfirmDate(date);
                i.setConfirmor(userId);
            });
            return quotaService.updateBatchById(updateList);
        } else {
            final List<Long> idList = JSON.parseArray(replyIds, Long.class);
            if (CollectionUtil.isEmpty(idList)) {
                throw new DaoException("The data is empty");
            }
            final List<BasicForcaseQuota> updateList = Lists.newArrayList();
            idList.forEach(i -> {
                final BasicForcaseQuota q = new BasicForcaseQuota();
                q.setId(i);
                q.setStatus(status);
                q.setRefusalReason(refusalReason);
                q.setConfirmDate(date);
                q.setConfirmor(userId);
                updateList.add(q);
            });
            return quotaService.updateBatchById(updateList);
        }
    }


    /**
     * 根据供应商导出回复Excel
     *
     * @param response     响应
     * @param planId       计划ID
     * @param supplierCode 供应商编码
     */
    public XSSFWorkbook bySupplierCodeExportReply(HttpServletRequest request, HttpServletResponse response,
                                                  Long planId, String supplierCode, Integer status, Long replyUserId,
                                                  String isRiskMaterial, String purchaseGroup) throws ParseException
    {
        List<BasicForcaseQuotaPO> replyDtoList = quotaService.bySupplierCodeGetReply(planId, supplierCode, replyUserId,
                isRiskMaterial, purchaseGroup, null);
        // 日期开始列下标
        int dateStartIndex = 6;//
        //增加风险物料标识
        if (replyUserId != null) {
            dateStartIndex = 7;
        }
        List<BasicForcaseQuotaPO> dateInterval = quotaService.findDateInterval(planId);

        final Map<String, List<BasicForcaseQuotaPO>> replyCodeMap = replyDtoList.stream()
                .collect(Collectors.toMap(t -> (t.getProductCode() + StringPool.UNDERSCORE + t.getSupplierCode())
                        , Lists::newArrayList, (t1, t2) -> {
                            t1.addAll(t2);
                            return t1;
                        }));
        System.out.println(JSON.toJSONString(replyCodeMap));


        // 发布量处理下标
        final int dateTitleIndex = dateStartIndex - 1;
        // Excel工作表格对象处理
        final XSSFWorkbook workbook = new XSSFWorkbook();
        final XSSFSheet sheet = workbook.createSheet();
        final List<String> titleUpList = Lists.newArrayList();
        final List<String> titleDownList = Lists.newArrayList();
        // 合并行处理
        final CellRangeAddress markRegion = new CellRangeAddress(0, 1, 0, 0);
        //final CellRangeAddress typeRegion = new CellRangeAddress(0, 1, 1, 1);
        final CellRangeAddress productRegion = new CellRangeAddress(0, 1, 1, 1);
        final CellRangeAddress productNameRegion = new CellRangeAddress(0, 1, 2, 2);
        final CellRangeAddress factoryCodeRegion = new CellRangeAddress(0, 1, 3, 3);
        final CellRangeAddress supplierRegion = new CellRangeAddress(0, 1, 4, 4);
        final CellRangeAddress supplierNameRegion = new CellRangeAddress(0, 1, 5, 5);
        final CellRangeAddress isRiskMaterialRegion = new CellRangeAddress(0, 1, 6, 6);
        sheet.addMergedRegion(markRegion);
        //sheet.addMergedRegion(typeRegion);
        sheet.addMergedRegion(productRegion);
        sheet.addMergedRegion(productNameRegion);
        sheet.addMergedRegion(factoryCodeRegion);
        sheet.addMergedRegion(supplierRegion);
        sheet.addMergedRegion(supplierNameRegion);
        if (replyUserId != null) {
            sheet.addMergedRegion(isRiskMaterialRegion);
        }
        // 隐藏第一列
        sheet.setColumnHidden(0, true);
        // 表头时间列处理
        for (int i = dateTitleIndex; i < dateInterval.size() + dateTitleIndex; i++) {
            final CellRangeAddress region = new CellRangeAddress(0, 0, dateTitleIndex + ((i - dateTitleIndex) * 3) + 1,
                    dateTitleIndex + ((i - dateTitleIndex) * 3) + 3);
            sheet.addMergedRegion(region);
            titleUpList.add(dateInterval.get(i - dateTitleIndex).getDateTime());
            titleDownList.add("分配数量");
            titleDownList.add("回复数量");
            titleDownList.add("回复日期");
        }

        // 写入表头
        final XSSFRow row1 = sheet.createRow(0);
        final XSSFRow row2 = sheet.createRow(1);
        final XSSFCell markCell = row1.createCell(0);
        //final XSSFCell typeCell = row1.createCell(1);
        final XSSFCell productCell = row1.createCell(1);
        final XSSFCell productNameCell = row1.createCell(2);
        final XSSFCell factoryCodeCell = row1.createCell(3);
        final XSSFCell supplierCell = row1.createCell(4);
        final XSSFCell supplierNameCell = row1.createCell(5);
        final XSSFCell isRiskMaterialCell = row1.createCell(6);
        final XSSFCell detail_remark = row1.createCell(32);
        markCell.setCellValue("mark");
        //typeCell.setCellValue("类型");
        productCell.setCellValue("物料编码");
        productNameCell.setCellValue("物料描述");
        factoryCodeCell.setCellValue("工厂编码");
        supplierCell.setCellValue("供应商编码");
        supplierNameCell.setCellValue("供应商名称");
        if (replyUserId != null) {
            isRiskMaterialCell.setCellValue("是否预警");
        }
        detail_remark.setCellValue("备注");
        String cal7Time = "";
        int ii = dateStartIndex;
        for (int i = dateStartIndex; i < titleUpList.size() + dateStartIndex; i++) {
            final XSSFCell row1Cell = row1.createCell(ii);
            String dateTime = titleUpList.get(i - dateStartIndex);
            if (cal7Time.equals("")) {
                row1Cell.setCellValue(dateTime);
            }
            ii = ii + 3;
        }
        for (int i = 0; i < titleDownList.size(); i++) {
            final XSSFCell row2Cell = row2.createCell(i + dateStartIndex);
            row2Cell.setCellValue(titleDownList.get(i));
        }
        // 写入数据
        int rowIndex = 2;
        for (String k : replyCodeMap.keySet()) {
            List<BasicForcaseQuotaPO> dataList = replyCodeMap.get(k);
            dataList.sort(Comparator.comparing(t -> LocalDate.parse(t.getDateTime())));

            final BasicForcaseQuotaPO itemData = dataList.get(0);
            final XSSFRow contentRow = sheet.createRow(rowIndex++);
            final XSSFCell markContentCell = contentRow.createCell(0);
            //final XSSFCell typeContentCodeCell = contentRow.createCell(1);
            final XSSFCell productContentCodeCell = contentRow.createCell(1);
            final XSSFCell productNameContentCodeCell = contentRow.createCell(2);
            final XSSFCell factoryCodeContentCodeCell = contentRow.createCell(3);
            final XSSFCell supplierContentCodeCell = contentRow.createCell(4);
            final XSSFCell supplierNameContentCodeCell = contentRow.createCell(5);
            final XSSFCell isRiskMaterialContentCodeCell = contentRow.createCell(6);

            markContentCell.setCellValue(itemData.getDetailId() + StringPool.UNDERSCORE + itemData.getSupplierCode());

            productContentCodeCell.setCellValue(itemData.getProductCode());
            productNameContentCodeCell.setCellValue(itemData.getProductName());
            factoryCodeContentCodeCell.setCellValue(itemData.getFactoryCode());
            supplierContentCodeCell.setCellValue(itemData.getSupplierCode());
            supplierNameContentCodeCell.setCellValue(itemData.getSupplierName());
            isRiskMaterialContentCodeCell.setCellValue(itemData.getIsRisk());

            for (int i = 0; i < dataList.size(); i++) {
                int a = (i * 3) + dateStartIndex;
                int b = (i * 3) + dateStartIndex + 1;
                int c = (i * 3) + dateStartIndex + 2;
                final XSSFCell releaseCell = contentRow.createCell(a);
                final XSSFCell replyCell = contentRow.createCell(b);
                final XSSFCell remarkCell = contentRow.createCell(c);
                releaseCell.setCellValue(
                        dataList.get(i).getDistributeNum() == null ? 0L : dataList.get(i).getDistributeNum()
                                .doubleValue());
                replyCell.setCellValue(
                        dataList.get(i).getReplyNum() == null ? 0L : dataList.get(i).getReplyNum().doubleValue());
                remarkCell.setCellValue(
                        dataList.get(i).getDetailRemark());
            }
        }
        return workbook;
    }


    /**
     * 回复导入
     *
     * @param file   文件
     * @param planId 计划ID
     * @param userId 操作人
     * @return 是否导入成功
     */
    public boolean replySuppImport(MultipartFile file, Long planId, String supplierCode, Long userId) {
        if (file == null) {
            throw new DaoException("请选择文件");
        }
        if (!file.getOriginalFilename().contains(".")) {
            throw new DaoException("文件类型不正确!");
        }
        // 解析excel获取集合
        final List<Map<String, String>> list = PoiExcel.readExcel(file, file.getOriginalFilename(), true, 0, 2);
        if (list == null || list.size() == 0) {
            throw new DaoException("数据不能为空");
        }

        final Map<String, List<BigDecimal>> dateMap = Maps.newHashMap();
        final Map<String, List<String>> DetailRemarkMap = new HashMap<>();
        final List<Long> detailIdList = Lists.newArrayList();
        list.forEach(item -> {
            final String mark = item.get("cell0");
            final List<String> DetailRemark = Lists.newArrayList();
            final List<BigDecimal> dataItem = Lists.newArrayList();
            //总列数加一
            int index = 32 + 13;
            int begin = 6;
            int jiou = 0;
            if (userId != null) {
                index = 33 + 13;
                begin = 7;
                jiou = 1;
            }
            for (int i = begin; i < index; i++) {
                if (i % 3 == jiou) {
                    dataItem.add(StrUtil.isEmpty(item.get("cell" + (i + 1))) ? BigDecimal.ZERO : new BigDecimal(
                            item.get("cell" + (i + 1))));
                    DetailRemark.add(
                            StrUtil.isEmpty(item.get("cell" + (i + 2))) ? StrUtil.EMPTY : item.get("cell" + (i + 2)));
                }
            }
            dateMap.put(mark, dataItem);
            DetailRemarkMap.put(mark, DetailRemark);
            detailIdList.add(Long.valueOf(mark.split("_")[0]));

        });

        final List<BasicForcaseQuota> updateReplyList = Lists.newArrayList();
        final Map<String, List<BasicForcaseQuotaPO>> codeReplyMap = quotaService.bySupplierCodeGetReply(planId,
                supplierCode, userId, null, null, detailIdList).stream().collect(Collectors.toMap(
                t -> (t.getDetailId() + StringPool.UNDERSCORE + t.getSupplierCode()),
                Lists::newArrayList,
                (t1, t2) -> {
                    t1.addAll(t2);
                    return t1;
                }));

        final LocalDateTime now = LocalDateTime.now();
        codeReplyMap.keySet().forEach(k -> {
            if (dateMap.containsKey(k)) {
                final List<BasicForcaseQuotaPO> replyList = codeReplyMap.get(k);
                replyList.sort(Comparator.comparing(t -> LocalDate.parse(t.getDateTime())));
                final List<BigDecimal> replyNumList = dateMap.get(k);
                final List<String> DetailRemark = DetailRemarkMap.get(k);
                //final String remark = replyRemarkMap.get(k);
                List<BasicForcaseQuota> signleRow = Lists.newArrayList();
                for (int i = 0; i < replyList.size(); i++) {
                    BasicForcaseQuotaPO dto = replyList.get(i);
                    final BasicForcaseQuota updateReply = new BasicForcaseQuota();
                    updateReply.setId(dto.getId());
                    updateReply.setAdjustNum(replyNumList.get(i));
                    updateReply.setAdjustDate(now);
                    updateReply.setStatus(BasicPlanConst.QuotaStatus.PENDING_REPLY);//已回复
                    updateReply.setDetailRemark(DetailRemark.get(i));
                    signleRow.add(updateReply);
                }
                updateReplyList.addAll(signleRow);
            }
        });

        if (updateReplyList.size() == 0) {
            throw new DaoException("待回复数据为空, 不进行回复导入");
        }
        return quotaService.updateBatchById(updateReplyList);

    }

    /**
     * 删除计划
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(Long id) {
        basicPlanService.removeById(id);
        // supplierTemplateQuestionnaireService.removeById(id);
        // supplierTemplateQuestionnaireItemService.remove(new LambdaQueryWrapper<SupplierTemplateQuestionnaireItem>
        // ().eq(
        //         SupplierTemplateQuestionnaireItem::getQuestionId, id));
    }

    @Override
    public void downloadSapData(Long planId, HttpServletRequest request, HttpServletResponse response) {
        final List<BasicForcaseDetail> shortageDetails = detailService.list(
                new LambdaQueryWrapper<>(BasicForcaseDetail.class).eq(BasicForcaseDetail::getPlanId, planId));
        List<BasicPlanSapExport> shortagePlanSapExports = Lists.newArrayList();
        for (BasicForcaseDetail shortageDetail : shortageDetails) {
            Map<String, Object> resultMap = Maps.newHashMap();
            final String productCode = shortageDetail.getProductCode();
            resultMap.put("productCode", productCode);
            final String productName = shortageDetail.getProductName();
            resultMap.put("productName", productName);
            final String productLongName = shortageDetail.getProductLongName();
            resultMap.put("productLongName", productLongName);
            final String factoryCode = shortageDetail.getFactoryCode();
            resultMap.put("factoryCode", factoryCode);
            final String purchaseGroup = shortageDetail.getPurchaseGroup();
            resultMap.put("purchaseGroup", purchaseGroup);
            resultMap.put("onPoNum", shortageDetail.getOnOrderNum().stripTrailingZeros().toPlainString());
            final List<BasicForcaseData> tmpList = dataService.list(
                    new LambdaQueryWrapper<>(BasicForcaseData.class).eq(BasicForcaseData::getDetailId,
                            shortageDetail.getId()).orderByAsc(BasicForcaseData::getId));
            for (int j = 0; j < 12; j++) {
                resultMap.put("m" + (j + 1), tmpList.get(j).getNum().stripTrailingZeros().toPlainString());
            }
            final BasicPlanSapExport e = BeanUtil.mapToBean(resultMap, BasicPlanSapExport.class, false);
            shortagePlanSapExports.add(e);
        }
        log.info("{}", shortagePlanSapExports);
        // 设置响应头信息
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String fileName = null;
        try {
            fileName = URLEncoder.encode("SAP原始数据", "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        // 使用EasyExcel导出Excel文件
        try {
            EasyExcel.write(response.getOutputStream()).head(BasicPlanSapExport.class)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .sheet("Sheet1").doWrite(shortagePlanSapExports);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}
