package com.bailuntec.job;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.bailuntec.domain.constant.CommonConstant;
import com.bailuntec.domain.constant.Constant;
import com.bailuntec.domain.dto.SalesDetailItem;
import com.bailuntec.domain.dto.SalesVolumeAvgDTO;
import com.bailuntec.domain.dto.SalesVolumeDTO;
import com.bailuntec.domain.entity.*;
import com.bailuntec.domain.enumerate.PlatformType;
import com.bailuntec.domain.example.*;
import com.bailuntec.mapper.*;
import com.bailuntec.service.AutoSalesService;
import com.bailuntec.service.AutoTurnoverService;
import com.bailuntec.service.impl.AutoSalesServiceImpl;
import com.bailuntec.service.impl.AutoTurnoverServiceImpl;
import com.bailuntec.service.impl.TurnoverDaysHandler;
import com.bailuntec.support.PointJob;
import com.bailuntec.utils.ListUtil;
import com.bailuntec.utils.SessionUtil;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static java.time.Period.between;

@Slf4j
public class AutoTurnoverJob extends PointJob {
    private AutoSalesService autoSalesService = new AutoSalesServiceImpl();
    private AutoTurnoverService autoTurnoverService = new AutoTurnoverServiceImpl();

    public static ThreadLocal<String> transferStrategy = new ThreadLocal<>();

    @Override
    public void executeJob(ShardingContext shardingContext, JobPointLog jobPointLog) {

        //分片需要处理的总条数
        long count = getTotalCount(shardingContext);
        log.info("当前分片需要处理的总条数：{}", count);
        //分片需要处理数据总页数
        int totalPage = getTotalPage(count, jobPointLog.getPageSize());
        log.info("当前分片分页任务分页查询页码大小：{}", jobPointLog.getPageSize());
        //
        log.info("当前分片需要处理的总页数：{}", totalPage);
        //
        AtomicInteger countItem = new AtomicInteger(0);

        for (int page = jobPointLog.getPageIndex(); page <= totalPage; page++) {

            //分页查SKU_仓库
            List<DcBaseStock> dcBaseStockList;
            try {
                DcBaseStockMapper baseStockMapperFor = SessionUtil.getSession().getMapper(DcBaseStockMapper.class);
                //这里是根据id排序,方法名没改了
                if (StringUtils.isBlank(shardingContext.getJobParameter())) {
                    dcBaseStockList = baseStockMapperFor.listStockByIdPart(shardingContext, (page - 1) * jobPointLog.getPageSize(), jobPointLog.getPageSize());
                } else {
                    switch (shardingContext.getJobParameter()) {
                        case "国内仓":
                        case "FBA仓":
                        case "海外仓":
                        case "第三方仓库":
                        case "耗材仓":
                            dcBaseStockList = baseStockMapperFor.listParameterStockById(shardingContext, (page - 1) * jobPointLog.getPageSize(), jobPointLog.getPageSize());
                            break;
                        case "ALL":
                            dcBaseStockList = baseStockMapperFor.listAllStock(shardingContext, (page - 1) * jobPointLog.getPageSize(), jobPointLog.getPageSize());
                            break;
                        case "NORMAL":
                            dcBaseStockList = baseStockMapperFor.listNormal(shardingContext, (page - 1) * jobPointLog.getPageSize(), jobPointLog.getPageSize());
                            break;
                        case "NE_NORMAL":
                            dcBaseStockList = baseStockMapperFor.listNeNormal(shardingContext, (page - 1) * jobPointLog.getPageSize(), jobPointLog.getPageSize());
                            break;
                        case "SPEC":
                            dcBaseStockList = baseStockMapperFor.listSpec(shardingContext, (page - 1) * jobPointLog.getPageSize(), jobPointLog.getPageSize());
                            break;
                        case "INCREMENT":
                            LocalDateTime v1 = LocalDateTime.of(LocalDate.now(), LocalTime.MIN).plusHours(3);
                            LocalDateTime v2 = LocalDateTime.of(LocalDate.now(), LocalTime.MIN).minusDays(7);
                            dcBaseStockList = baseStockMapperFor.increment(shardingContext, v1, v2, (page - 1) * jobPointLog.getPageSize(), jobPointLog.getPageSize());
                            break;
                        case Constant.OCEAN_HEAD:
                            dcBaseStockList = baseStockMapperFor.selectAir(shardingContext, (page - 1) * jobPointLog.getPageSize(), jobPointLog.getPageSize());
                            break;
                        case Constant.AIR_HEAD:
                            dcBaseStockList = baseStockMapperFor.selectOcean(shardingContext, (page - 1) * jobPointLog.getPageSize(), jobPointLog.getPageSize());
                            break;
                        default:
                            dcBaseStockList = baseStockMapperFor.listWarehouseStockById(shardingContext, shardingContext.getJobParameter(), (page - 1) * jobPointLog.getPageSize(), jobPointLog.getPageSize());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("Mybatis操作DB分页查询失败", e);
            } finally {
                SessionUtil.closeSession();
            }

            if (dcBaseStockList != null && dcBaseStockList.size() > 0) {

                int finalPage1 = page;
                dcBaseStockList
                        .stream()
                        .parallel()
                        .forEach(dcBaseStock -> {
                            Integer finalPage = finalPage1;
                            log.info("当前页码：{},当前线程:{}，当前sku:{},当前记录数：{}", finalPage,
                                    Thread.currentThread().getName()
                                    , dcBaseStock.getBailunSku() + "_" + dcBaseStock.getWarehouseCode()
                                    , countItem.incrementAndGet());

                            try {
                                String queryTime = DateTimeFormatter.ofPattern(CommonConstant.DATE_FORMAT).format(LocalDate.now());
                                dcBaseStock = SessionUtil.getSession().getMapper(DcBaseStockMapper.class).selectOneByExample(DcBaseStockExample.newAndCreateCriteria()
                                        .andBailunSkuEqualTo(dcBaseStock.getBailunSku())
                                        .andWarehouseCodeEqualTo(dcBaseStock.getWarehouseCode())
                                        .example());
                                this.autoTurnoverFromStock(dcBaseStock);
                            } catch (Exception e) {
                                try {
                                    DcAutoException dcAutoException = new DcAutoException();
                                    dcAutoException.setBailunSku(dcBaseStock.getBailunSku());
                                    dcAutoException.setWarehouseCode(dcBaseStock.getWarehouseCode());
                                    dcAutoException.setMessage(e.getMessage() + e.getStackTrace().toString());
                                    DcAutoExceptionMapper dcAutoExceptionMapper = SessionUtil.getSession().getMapper(DcAutoExceptionMapper.class);

                                    dcAutoExceptionMapper.insert(dcAutoException);
                                } catch (Exception ignore) {

                                }

                            } finally {
                                SessionUtil.closeSession();
                            }
                        });
            }
        }

    }

    private long getTotalCount(ShardingContext shardingContext) {
        try {
            DcBaseStockMapper baseStockMapper = SessionUtil.getSession().getMapper(DcBaseStockMapper.class);
            if (StringUtils.isBlank(shardingContext.getJobParameter())) {
                return baseStockMapper.countByExample(DcBaseStockExample.newAndCreateCriteria().example());
            } else {
                switch (shardingContext.getJobParameter()) {
                    case "国内仓":
                    case "FBA仓":
                    case "海外仓":
                    case "第三方仓库":
                    case "耗材仓":
                        return baseStockMapper.countByParam(shardingContext);
                    case "ALL":
                        return baseStockMapper.countAll(shardingContext);
                    //普通任务
                    case "NORMAL":
                        return baseStockMapper.countNormal(shardingContext);
                    case "NE_NORMAL":
                        return baseStockMapper.countNeNormal(shardingContext);
                    case "SPEC":
                        return baseStockMapper.countSpec(shardingContext);
                    case "INCREMENT":
                        LocalDateTime v1 = LocalDateTime.of(LocalDate.now(), LocalTime.MIN).plusHours(3);
                        LocalDateTime v2 = LocalDateTime.of(LocalDate.now(), LocalTime.MIN).minusDays(7);
                        return baseStockMapper.countIncrement(shardingContext, v1, v2);
                    case Constant.OCEAN_HEAD:
                        return baseStockMapper.countAir(shardingContext);
                    case Constant.AIR_HEAD:
                        return baseStockMapper.countOcean(shardingContext);
                    default:
                        return baseStockMapper.countByWarehouseCode(shardingContext);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Mybatis操作DB查询总页数失败", e);
        } finally {
            SessionUtil.closeSession();
        }
    }

    public void autoTurnoverFromStock(DcBaseStock dcBaseStock) {

        String queryTime = DateTimeFormatter.ofPattern(CommonConstant.DATE_FORMAT).format(LocalDate.now());
        this.autoTurnoverFromStock(queryTime, dcBaseStock);

        DcBaseWarehouseMapper dcBaseWarehouseMapper = SessionUtil.getSession().getMapper(DcBaseWarehouseMapper.class);
        DcBaseWarehouse dcBaseWarehouse = dcBaseWarehouseMapper.selectByWarehouseCode(dcBaseStock.getWarehouseCode());
        if (!"国内仓".equals(dcBaseWarehouse.getHqType())) {

            transferStrategy.set(Constant.OCEAN_HEAD);
            this.autoTurnoverFromStock(queryTime, dcBaseStock);
            transferStrategy.remove();

            transferStrategy.set(Constant.AIR_HEAD);
            this.autoTurnoverFromStock(queryTime, dcBaseStock);
            transferStrategy.remove();
        }
    }

    /**
     * <p>
     * 核心方法
     * 基于库存计算自动周转
     * </p>
     *
     * @param queryTime   时间
     * @param dcBaseStock 库存
     */
    private void autoTurnoverFromStock(String queryTime, DcBaseStock dcBaseStock) {
        //sku
        String bailunSku = dcBaseStock.getBailunSku();
        //仓库编码
        String warehouseCode = dcBaseStock.getWarehouseCode();
        //销量
        DcAutoSales dcAutoSales;
        //仓库
        DcBaseWarehouse dcBaseWarehouse;
        try {
            //库存mapper
            DcBaseWarehouseMapper baseWarehouseMapper = SessionUtil.getSession().getMapper(DcBaseWarehouseMapper.class);
            //库存对应的仓库
            dcBaseWarehouse = baseWarehouseMapper.selectByWarehouseCode(warehouseCode);

            BigDecimal incrementalRatio = BigDecimal.ONE;
            int compareSales = 0;
            //销量
            DcAutoSalesMapper autoSalesMapper;
            //jb
            SalesVolumeDTO salesVolumeDTO;
            //销量mapper
            autoSalesMapper = SessionUtil.getSession().getMapper(DcAutoSalesMapper.class);
            // 获取7、14日均销量
            BigDecimal agvSales = autoSalesMapper.getSalesAvg(queryTime, bailunSku, warehouseCode);
            //销量突增检测mapper
            DcAutoUprushConfigMapper dcAutoUprushConfigMapper = SessionUtil.getSession().getMapper(DcAutoUprushConfigMapper.class);
            DcAutoUprushConfig dcAutoUprushConfig = null;
            //如果仓库不为空
            if (dcBaseWarehouse != null) {
                //销量突增检测
                dcAutoUprushConfig = dcAutoUprushConfigMapper.selectOneByWarehouse(warehouseCode, dcBaseWarehouse.getHqType());
            }
            if (dcAutoUprushConfig != null && agvSales != null) {
                // 突增销量 = (7+14日均销量)/2 * 突增率
                incrementalRatio = incrementalRatio.add(dcAutoUprushConfig.getPercentage());
                compareSales = agvSales.multiply(incrementalRatio).setScale(0, RoundingMode.UP).intValue();
            }
            autoSalesMapper = SessionUtil.getSession().getMapper(DcAutoSalesMapper.class);
            if (compareSales == 0) {
                salesVolumeDTO = autoSalesMapper.getSalesVolumeDTONoCompare(queryTime, bailunSku, warehouseCode);
            } else {
                salesVolumeDTO = autoSalesMapper.getSalesVolumeDTO(queryTime, bailunSku, warehouseCode, compareSales);
            }
            if (StringUtils.isEmpty(salesVolumeDTO.getHistorySales().trim())) {
                DcAutoSalesMapper dcAutoSalesMapper = SessionUtil.getSession().getMapper(DcAutoSalesMapper.class);
                dcAutoSales = new DcAutoSales(bailunSku, warehouseCode);
                int i = dcAutoSalesMapper.updateByExampleSelective(dcAutoSales, DcAutoSalesExample
                        .newAndCreateCriteria()
                        .andBailunSkuEqualTo(dcAutoSales.getBailunSku())
                        .andWarehouseCodeEqualTo(dcAutoSales.getWarehouseCode()).example());
                if (i == 0) {
                    dcAutoSalesMapper.insertSelective(dcAutoSales);
                }
            } else {
                dcAutoSales = autoSalesService.forecastSalesBySalesVolumeDTO(salesVolumeDTO);
            }
            this.autoTurnoverUseDcAutoSales(bailunSku, warehouseCode, dcAutoSales, dcBaseStock, dcBaseWarehouse);

        } finally {
            SessionUtil.closeSession();
        }

    }

    /**
     * <p>
     * 将历史销量,dcAutoSales
     * 根据历史销量拟合的销量函数曲线,dcAutoSales
     * 库存信息传入,dcBaseStock
     * 计算自动周转
     * </p>
     *
     * @param bailunSku       百伦sku
     * @param warehouseCode   仓库编码
     * @param dcAutoSales     销量
     * @param dcBaseStock     库存
     * @param dcBaseWarehouse 仓库
     */
    private void autoTurnoverUseDcAutoSales(String bailunSku,
                                            String warehouseCode,
                                            DcAutoSales dcAutoSales,
                                            DcBaseStock dcBaseStock,
                                            DcBaseWarehouse dcBaseWarehouse) {

        //记录时间
        LocalDate recordTime = LocalDate.now();
        DcAutoTurnoverMapper dcAutoTurnoverMapper = SessionUtil.getSession().getMapper(DcAutoTurnoverMapper.class);
        DcAutoTurnoverWithBLOBs dcAutoTurnover = dcAutoTurnoverMapper.selectOneByExampleWithBLOBs(DcAutoTurnoverExample.newAndCreateCriteria()
                .andBailunSkuEqualTo(bailunSku)
                .andWarehouseCodeEqualTo(warehouseCode)
                .example());

        if (dcAutoTurnover == null) {
            dcAutoTurnover = new DcAutoTurnoverWithBLOBs();
        }
        dcAutoTurnover.setBailunSku(bailunSku);
        dcAutoTurnover.setWarehouseCode(warehouseCode);
        //写入仓库名
        if (dcBaseWarehouse != null) {
            dcAutoTurnover.setWarehouseName(dcBaseWarehouse.getWarehouseName());
            if (dcBaseWarehouse.getSystemFlag().toUpperCase().equals(PlatformType.FBA.value())) {
                dcAutoTurnover.setWarehouseName(dcBaseWarehouse.getBailunAccount());
            }
        }


        /*
         * 通过公式计算周转天数
         * 1. 拿到供应商交期-turnoverSku和百伦分类Id-bailunFirstLevelCatagoryId
         * 2. 获取调拨头程 + 调拨打包 + 海外仓入库天数 的配置 dcAutoConfigDelivery
         * 3. 拿到仓库名称 和 仓库类型(可选) turnoverWarehouse
         * 周转天数计算公式:
         * 供应链长度 = 周转天数 = 供应商交期 + 入库时间(2天质检入库时间) + 调拨头程  + 调拨打包  + 海外仓入库 + 财务付款天数 + 采购平均下单天数
         */
        DcBaseSku turnoverSku;
        //从采购平均指标,仓库平均指标拿周转天数, 拿不到的或者数值异常的, 才按上面的来
        DcAveragePurchase dcAveragePurchase;
        DcAverageWarehouse dcAverageWarehouse;


        //财务付款天数 是否先款后货? 先款后货供应链长度加一天paymentBeforeDelivery = 1;
        Integer paymentBeforeDelivery = 0;
        Integer purchaseCreateOrderDays = 0;

        DcBaseSkuMapper baseSkuMapper = SessionUtil.getSession().getMapper(DcBaseSkuMapper.class);
        turnoverSku = baseSkuMapper.selectOneByExample(DcBaseSkuExample.newAndCreateCriteria().andBailunSkuEqualTo(bailunSku).example());

        DcAveragePurchaseMapper dcAveragePurchaseMapper = SessionUtil.getSession().getMapper(DcAveragePurchaseMapper.class);
        DcAverageWarehouseMapper dcAverageWarehouseMapper = SessionUtil.getSession().getMapper(DcAverageWarehouseMapper.class);

        dcAveragePurchase = dcAveragePurchaseMapper.selectOneByExample(DcAveragePurchaseExample.newAndCreateCriteria().andBailunSkuEqualTo(bailunSku).andWarehouseCodeEqualTo(warehouseCode).andSupplierIdEqualTo(turnoverSku.getSuppliersId()).example());
        dcAverageWarehouse = dcAverageWarehouseMapper.selectOneByExample(DcAverageWarehouseExample.newAndCreateCriteria().andBailunSkuEqualTo(bailunSku).andWarehouseCodeEqualTo(warehouseCode).example());

        if (dcAveragePurchase != null) {
            paymentBeforeDelivery = dcAveragePurchase.getPaymentBeforeDelivery();
            purchaseCreateOrderDays = dcAveragePurchase.getCreateOrderDays();
        }


        Integer bailunFirstLevelCatagoryId = null; //百伦第一级分类Id, 可以为null
        // 获取调拨头程 + 调拨打包 + 海外仓入库天数 的配置
        DcAutoConfigDelivery dcAutoConfigDelivery = getDcAutoConfigDelivery(bailunSku, warehouseCode, dcBaseWarehouse);

        //处理周转天数
        TurnoverDaysHandler turnoverDaysHandler = new TurnoverDaysHandler(dcAveragePurchase, turnoverSku, dcBaseWarehouse, dcAutoTurnover, dcAverageWarehouse, dcAutoConfigDelivery);

        turnoverDaysHandler.handleTurnoverDays();
        int turnoverDays = dcAutoTurnover.getTurnoverDays();

        /*
         * 因为第一个周转期再补货已经来不及
         * 所以预测2倍周转期长度
         * 2倍周转期长度 < 31 的, 默认算31天, 但是生成的采购建议不拿2倍周转期以外的
         */
        SalesDayConfigMapper salesDayConfigMapper = SessionUtil.getSession().getMapper(SalesDayConfigMapper.class);
        SalesDayConfig farthestSalesDayConfig = salesDayConfigMapper.selectFarthest(bailunSku, warehouseCode);
        int autoForecastDay = turnoverDays * Constant.TURNOVER_MULTIPLE < Constant.MIN_AUTO_FORECAST_DAY ? Constant.MIN_AUTO_FORECAST_DAY : turnoverDays * 2;

        if (farthestSalesDayConfig != null) {
            int salesDayConfigDays = Period.between(LocalDate.now(), farthestSalesDayConfig.getDate().toLocalDate()).getDays();
            autoForecastDay = Math.max(salesDayConfigDays, autoForecastDay);
        }

        /*
         * 初始化预测容器, 用来存放预测销量, 库存等数据
         * forecastInventoryList
         * forecastPurchaseInboundList
         * forecastTransferInboundList
         * forecastInboundList
         * forecastSalesList
         * forecastShortSupplyList
         */
        //预测采购入库
        List<Integer> forecastPurchaseInboundList = new ArrayList<>(autoForecastDay);
        //预测调拨入库
        List<Integer> forecastTransferInboundList = new ArrayList<>(autoForecastDay);
        //预测库存
        List<BigDecimal> forecastInventoryList = new ArrayList<>(autoForecastDay);
        //预测入库
        List<Integer> forecastInboundList = new ArrayList<>(autoForecastDay);
        //预测入库释义
        List<String> forecastInboundRelationList = new ArrayList<>(autoForecastDay);
        //预测销量
        List<BigDecimal> forecastSalesList = new ArrayList<>(autoForecastDay);
        //预测销量释义
        List<String> forecastSalesExplainList = new ArrayList<>(forecastSalesList.size());
        //七天实际断货+预测断货
        List<BigDecimal> forecastShortSupplyList = new ArrayList<>(autoForecastDay);
        //预测采购建议
        List<BigDecimal> forecastPurchaseAdvisedList = new ArrayList<>(autoForecastDay);
        //moq数
        List<BigDecimal> moqPurchaseAdvisedList = new ArrayList<>(autoForecastDay);

        Map<String, BigDecimal> saveDaysMap = new HashMap<>();

        initForecastList(forecastInboundRelationList, forecastInventoryList, forecastPurchaseInboundList, forecastTransferInboundList, forecastInboundList, forecastSalesList, forecastShortSupplyList, autoForecastDay, forecastSalesExplainList);

        /*
         * 去销量配置表dc_auto_config_sales_upper_limit找销量最大值
         * 1. 根据仓库SKU找销量上限, 有就停止, 没有则2
         * 2. 根据仓库和分类找销量上限, 有就停止, 没有则3
         * 3. 根据倍数算上限
         * 4. 根据加权平均算上限
         */
        BigDecimal salesUpperLimit = calculateSalesUpperLimit(bailunSku, warehouseCode, bailunFirstLevelCatagoryId, dcAutoSales);


        /*
         * 线性回归算法: 过去30天销量	根据这个预测销量走势是上升还是下降，并量化到数值
         * 均值, 配置安全库存法: 销量上下限	根据过去30天销量，计算出合理的销量封顶值和销量最低值
         * 曲线拟合 :销量预测, 根据销量走势和上下限，计算出销量预测曲线, 如果历史销量包含0, 无法使用指数函数拟合
         */
        long time = System.currentTimeMillis();
        log.info(Long.toString(time));
        forecastSalesList = autoTurnoverService.getAutoTurnoverSaleDetails(dcAutoTurnover, turnoverDays, autoForecastDay);
        List<String> forecastSalesListFormula = autoTurnoverService.getAutoTurnoverSaleDetailsFormula(dcAutoTurnover, turnoverDays, autoForecastDay);
        log.info(Long.toString(System.currentTimeMillis() - time));

        AtomicReference<LocalDate> localDate = new AtomicReference<>(LocalDate.now());

        List<SalesDetailItem> salesDetailItemList = forecastSalesList.stream().map(salesDetail -> {
            SalesDetailItem salesDetailItem = new SalesDetailItem();
            salesDetailItem.setSales(salesDetail);
            salesDetailItem.setDate(localDate.get());
            localDate.set(localDate.get().plusDays(1));
            return salesDetailItem;
        }).collect(Collectors.toList());

        DcBaseWeekCoefficientMapper dcBaseWeekCoefficientMapper = SessionUtil.getSession().getMapper(DcBaseWeekCoefficientMapper.class);

        List<DcBaseWeekCoefficient> dcBaseWeekCoefficientList = dcBaseWeekCoefficientMapper.selectByDays(salesDetailItemList, dcAutoTurnover.getBailunSku(), dcAutoTurnover.getWarehouseCode());

        SalesDayConfigMapper salesDayConfigMapper1 = SessionUtil.getSession().getMapper(SalesDayConfigMapper.class);
        List<SalesDayConfig> salesDayConfigMapperList = salesDayConfigMapper1.selectByDays(salesDetailItemList, dcAutoTurnover.getBailunSku(), dcAutoTurnover.getWarehouseCode());

        if (ListUtil.isNotEmpty(dcBaseWeekCoefficientList)) {
            Map<LocalDate, DcBaseWeekCoefficient> weekCoefficientMap = dcBaseWeekCoefficientList.stream().collect(Collectors.toMap(DcBaseWeekCoefficient::getWeekDate, dcBaseWeekCoefficient -> dcBaseWeekCoefficient));
            salesDetailItemList.forEach(salesDetailItem -> {
                DcBaseWeekCoefficient coefficient = weekCoefficientMap.get(salesDetailItem.getDate());
                if (coefficient != null) {
                    salesDetailItem.setWeekCoefficient(new BigDecimal(coefficient.getCoefficient()));
                }
            });
        }
        if (ListUtil.isNotEmpty(salesDayConfigMapperList)) {
            Map<LocalDate, SalesDayConfig> salesDayConfigMap = salesDayConfigMapperList.stream().collect(Collectors.toMap(salesDayConfig -> salesDayConfig.getDate().toLocalDate(), salesDayConfig -> salesDayConfig));
            salesDetailItemList.forEach(salesDetailItem -> {
                SalesDayConfig salesDayConfig = salesDayConfigMap.get(salesDetailItem.getDate());
                if (salesDayConfig != null) {
                    salesDetailItem.setSalesDayConfig(salesDayConfig.getSales());
                }
            });
        }
        salesDetailItemList.forEach(SalesDetailItem::calcShowSales);

        dcAutoTurnover.setGmtModified(LocalDateTime.now());
        forecastSalesList = salesDetailItemList.stream().map(SalesDetailItem::getShowSales).collect(Collectors.toList());


        /*
         * 处理矫正信息
         * 主要是根据市场行情，人为的干预预测结果
         *  例如圣诞档期，圣诞之前销量会连续上涨，但是过了23号之后，就会急速下跌
         * 这种情况就要人工设定一条增益曲线，如果想放大预测，曲线值大于1，如果想缩减预测，曲线值小于1
         * 然后跟基本预测销量相乘，到到最终的预测销量
         */
        correctionForecastSales(recordTime, autoForecastDay, dcAutoSales, forecastSalesList);

        /*
         * 活动因素叠加
         * 主要是根据活动排期，人为干预销量预测结果W
         * 例如报Deal，时间两天，预估这两天因为deal会每天带来100单额外销量
         * 那按照日期把额外的销量配置出来，将预测销量跟额外销量相加得到最终销量
         */
        StringBuilder promotionsBuilder = promotionForecastSales(recordTime, autoForecastDay, dcAutoSales, forecastSalesList);


        Integer purchase = 0;//采购在途
        Integer transfer = 0;//调拨在途
        Integer outStock = 0;//缺货数
        DcMidTransit dcMidTransit;
        try {
            DcMidTransitMapper midTransitMapper = SessionUtil.getSession().getMapper(DcMidTransitMapper.class);
            dcMidTransit = midTransitMapper.selectOneByExample(DcMidTransitExample.newAndCreateCriteria().andBailunSkuEqualTo(bailunSku).andWarehouseCodeEqualTo(warehouseCode).example());
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Mybatis操作DB失败", e);
        } finally {
            SessionUtil.closeSession();
        }

        if (dcMidTransit != null) {
            purchase = dcMidTransit.getQuantityPurchase();
            //todo
            transfer = dcMidTransit.getQuantityTransfer();
            outStock = dcMidTransit.getQuantityOutStock();
        }
        /*
         * 拿到预计到货时间在周转期内的采购单
         * 采购系统的预计到货只是到货时间
         * 还需要加上入库时长,
         * 且不同交通方式的入库时长不一致
         */
        ExpectedStorage(dcAutoConfigDelivery, forecastInboundRelationList, purchase, transfer, recordTime, dcAutoTurnover, forecastPurchaseInboundList, forecastTransferInboundList, forecastInboundList);

        //通过SKU和仓库拿到实际库存
        Integer realInventory = (dcBaseStock != null && dcBaseStock.getUsableStock() != null) ? dcBaseStock.getUsableStock() : 0;
        /*
         * 通过SKU拿到供应商Id,
         * 再去dc_average_purchase拿到平均交期
         * 平均交期 - 供应商交期 > 0  就是平均逾期天数
         *
         * 平均逾期天数 * 加权平均销量(去最大值最小值)
         * 为安全库存--即防止逾期缓冲所需库存
         * 可以支持配置
         * 2019-04-22更新 不管逾期
         */
        BigDecimal totalSafeInventory = calculateSafeInventory(turnoverSku,
                bailunSku,
                warehouseCode,
                bailunFirstLevelCatagoryId,
                dcAutoSales,
                JSON.toJSONString(forecastSalesList),
                dcAutoTurnover,
                saveDaysMap);

        Integer moq = turnoverSku.getMoq();
        /*
         * 预测库存 = 前一日库存 - 前一日销量(没有考虑实际缺货)
         *
         * 采购建议为周转期后那一天的销量 + 安全库存 - 预测库存 + 累计断货
         * 如果有库存, 优先抵消断货数, 再去跟销量对比
         */
        //通过SKU和仓库取预测销量
        Integer inventoryAvailableDays = 0;
        BigDecimal totalSales = BigDecimal.ZERO;//周转期总销量
        BigDecimal totalInbound = BigDecimal.ZERO;//周转期总预计入库
        BigDecimal moqDecimal = BigDecimal.valueOf(moq.longValue());
        List<Integer> historySalesList = JSON.parseObject(dcAutoSales.getHistorySalesDetails(), new TypeReference<List<Integer>>() {
        });

        /*
         * dcAutoConfigSkuWarehouse不存在或者其status=0都是监控状态
         * 库存为0 并且 最近14天日均销量为0
         * 已经提醒实际缺货的，预计缺货就不提醒了
         * 已经提醒实际冗余的，预计冗余就不提醒了
         * 停止监控的不提醒
         */
        Integer historyFourteenSales = 0;
        Integer historySevenSales = 0;
        Integer historyThreeSales = 0;
        Integer historyThirtySales = 0;
        for (int i = 0; i < 30; i++) {
            if (i < 3) {
                historyThreeSales += historySalesList.get(27 + i);
            }
            if (i < 7) {
                historySevenSales += historySalesList.get(23 + i);
            }
            if (i < 14) {
                historyFourteenSales += historySalesList.get(16 + i);
            }
            historyThirtySales += historySalesList.get(i);
        }
        BigDecimal avgHistoryThreedaySales = BigDecimal.valueOf(historyThreeSales.longValue()).divide(Constant.BIGDECIMAL_THREE, 3, RoundingMode.HALF_EVEN);
        BigDecimal avgHistorySevendaySales = BigDecimal.valueOf(historySevenSales.longValue()).divide(Constant.BIGDECIMAL_SEVEN, 3, RoundingMode.HALF_EVEN);
        BigDecimal avgHistoryFourteendaySales = BigDecimal.valueOf(historyFourteenSales.longValue()).divide(Constant.BIGDECIMAL_FOURTEEN, 3, RoundingMode.HALF_EVEN);
        BigDecimal avgHistoryThirtydaySales = BigDecimal.valueOf(historyThirtySales.longValue()).divide(Constant.BIGDECIMAL_THIRTY, 3, RoundingMode.HALF_EVEN);

        /*
         * forecastFluctuationList 用于存放预计缺货 实际缺货 预计冗余  实际冗余的数据
         * dcAutoConfigSkuWarehouse
         * hasMonitorFluctuation 是否停止监控波动功能
         */
        List<DcAutoForecastFluctuation> forecastFluctuationList = new ArrayList<>(turnoverDays);
        //看是否监控状态
        DcAutoConfigSkuWarehouse dcAutoConfigSkuWarehouse = this.getAutoConfigSkuWarehouse(bailunSku, warehouseCode);
        //是否开启监控波动功能
        boolean hasMonitorFluctuation = actualForecastFluctuations(dcAutoTurnover,
                forecastSalesList,
                realInventory,
                totalSafeInventory,
                dcAutoConfigSkuWarehouse,
                avgHistoryFourteendaySales
        );



        /*
         * 获取采购建议-FBA仓库
         *  实际缺货 + 总销量 - 总预计入库  + (安全库存 - 预测库存) + 周转天的销量
         */
        if (dcBaseWarehouse.getSystemFlag().toUpperCase().equals(PlatformType.FBA.value())) {
            /*
             * fba仓库新增一套规则
             * 1. fba缺货 销量需要变成0 因为没货就真的是0了
             * 2. 到货后，离缺货日期如果有N天， 每天预计销量比原来 递减7%   如果缺货了10天 10天后到货，也就是10天后的预计销量 （1-0.07）的10次方，但是这个只是预测
             * 3. 如果到货后有真实销量，迅速用真实销量的曲线替代这个7%递减的曲线，这个递减曲线的影响马上清0
             * 4. 通常缺货后，我们会用一个sku跟卖，这个sku销量会很高   我们的处理方法是，对这个sku做特殊销售规则 （这个人为做就行）
             */
            inventoryAvailableDays = fbaAdvise(turnoverSku,
                    warehouseCode,
                    dcAutoTurnover,
                    turnoverDays,
                    autoForecastDay,
                    forecastInventoryList,
                    forecastInboundList,
                    forecastSalesList,
                    salesDetailItemList,
                    forecastShortSupplyList,
                    forecastPurchaseAdvisedList,
                    moqPurchaseAdvisedList,
                    realInventory,
                    totalSafeInventory,
                    outStock,
                    inventoryAvailableDays,
                    totalSales,
                    totalInbound,
                    moqDecimal,
                    historySalesList,
                    forecastSalesExplainList);

        } else {
            /*
             * 获取采购建议-普通仓库
             *  实际缺货 + 总销量 - 总预计入库  + (安全库存 - 预测库存) + 周转天的销量
             */
            inventoryAvailableDays = commonAdvise(turnoverSku,
                    bailunSku,
                    warehouseCode,
                    dcAutoTurnover,
                    turnoverDays,
                    autoForecastDay,
                    forecastInventoryList,
                    forecastInboundList,
                    forecastSalesList,
                    forecastShortSupplyList,
                    forecastPurchaseAdvisedList,
                    moqPurchaseAdvisedList,
                    realInventory,
                    totalSafeInventory,
                    outStock,
                    inventoryAvailableDays,
                    totalSales,
                    totalInbound,
                    moqDecimal,
                    saveDaysMap);
        }

        if (hasMonitorFluctuation) {
            DcAutoForecastFluctuation dcAutoForecastFluctuationActual = null;
            BigDecimal forecastPurchaseAdvise = forecastPurchaseAdvisedList.get(0);

            if (forecastPurchaseAdvise.compareTo(BigDecimal.ZERO) < 0) {

                //实际冗余
                if (forecastPurchaseAdvise.add(dcAutoTurnover.getTurnoverInbound()).compareTo(BigDecimal.ZERO) < 0) {
                    dcAutoForecastFluctuationActual = new DcAutoForecastFluctuation();
                    //实际冗余
                    dcAutoForecastFluctuationActual.setType(3);
                    dcAutoForecastFluctuationActual.setQuantity(Math.abs(forecastPurchaseAdvise.add(dcAutoTurnover.getTurnoverInbound()).intValue()));
                }
                //预计冗余
                else {
                    dcAutoForecastFluctuationActual = new DcAutoForecastFluctuation();
                    //实际冗余
                    dcAutoForecastFluctuationActual.setType(4);
                    dcAutoForecastFluctuationActual.setQuantity(Math.abs(forecastPurchaseAdvise.intValue()));
                }
            }

            if (outStock > 0) {
                dcAutoForecastFluctuationActual = new DcAutoForecastFluctuation();
                //实际缺货
                dcAutoForecastFluctuationActual.setType(1);
                dcAutoForecastFluctuationActual.setQuantity(outStock);
            }

            if (dcAutoForecastFluctuationActual != null) {
                dcAutoForecastFluctuationActual.setAvgSalesThree(avgHistoryThreedaySales);
                dcAutoForecastFluctuationActual.setStartTime(recordTime);
                dcAutoForecastFluctuationActual.setEndTime(recordTime);
                forecastFluctuationList.add(dcAutoForecastFluctuationActual);
            }

            //自动下单管理的冗余-缺货等异常提醒
            unusualFluctuation(
                    turnoverDays,
                    recordTime,
                    dcAutoConfigSkuWarehouse,
                    turnoverSku,
                    warehouseCode,
                    dcAutoTurnover,
                    avgHistoryThreedaySales,
                    forecastFluctuationList,
                    forecastShortSupplyList,
                    dcBaseStock);
        } else {
            try {
                DcAutoForecastFluctuationMapper dcAutoForecastFluctuationMapper = SessionUtil.getSession().getMapper(DcAutoForecastFluctuationMapper.class);
                dcAutoForecastFluctuationMapper.deleteByExample(DcAutoForecastFluctuationExample.newAndCreateCriteria()
                        .andBailunSkuEqualTo(bailunSku)
                        .andWarehouseCodeEqualTo(warehouseCode)
                        .example());
            } finally {
                SessionUtil.closeSession();
            }
        }

        //拿一下2倍周转期内的入库总数
        Integer quantityInbound = 0;
        for (int i1 = 0; i1 < Constant.TURNOVER_MULTIPLE * turnoverDays; i1++) {
            quantityInbound += forecastInboundList.get(i1);
        }
        /*
         * 集合数据持久化到DB
         */
        assignmentToForecastSales(bailunSku, warehouseCode, forecastSalesList);
        assignmentToForecastInventory(bailunSku, warehouseCode, realInventory, forecastInventoryList);
        assignmentToForecastShortSupply(bailunSku, warehouseCode, forecastShortSupplyList);
        assignmentToForecastInbound(bailunSku, warehouseCode, realInventory, forecastInboundList, forecastInboundRelationList);

        /*
         * 周转表加一个，备货天数的额外销量(建议数累加预测销量)
         * 如果备货天数小于周转天数, 没问题
         * 如果备货天数大于周转天数, 采购建议数都没生成那么多天, 取索引为turnoverDays的
         */
        Integer stockUpDays = getStockUpDays(warehouseCode);
        /*
         * 自动周转表的预测销量 要加上7天真实销量
         * 自动周转表的预测入库 要加上7天真实入库 - TODO
         * 自动周转表的预测库存 要加上7天真实库存 - TODO
         * 自动周转表的预测缺货数 要加上7天真实缺货数 - TODO
         * 计算过去7天，14天日均销量, 展示用
         * 计算1日,7日,14日平均预测销量, 展示用
         */
        BigDecimal avgForecastOnedaySales = forecastSalesList.get(0);
        BigDecimal avgForecastSevendaySales = BigDecimal.ZERO;
        BigDecimal avgForecastFourteendaySales = BigDecimal.ZERO;
        BigDecimal avgForecastTurnoverDaySales = BigDecimal.ZERO;
        BigDecimal stockUpSales = BigDecimal.ZERO;
        int forturnoverDays = Math.max(turnoverDays, 14);

        for (int i = 0; i < forturnoverDays; i++) {
            if (i < 7) {
                avgForecastSevendaySales = avgForecastSevendaySales.add(forecastSalesList.get(i));
                forecastSalesList.add(i, BigDecimal.valueOf(historySalesList.get(23 + i)));
                forecastSalesListFormula.add(i, StringUtils.SPACE);
                forecastSalesExplainList.add(i, "历史销量");
                forecastInboundList.add(i, 0);
                forecastInventoryList.add(i, BigDecimal.ZERO);
                forecastShortSupplyList.add(i, BigDecimal.ZERO);
            }
            if (i < 14) {
                avgForecastFourteendaySales = avgForecastFourteendaySales.add(forecastSalesList.get(i));
            }
            avgForecastTurnoverDaySales = avgForecastTurnoverDaySales.add(forecastSalesList.get(i));
            //如果初始采购建议 <= 0, 那就也不用备货., stockUpDays - 1的原因是我们已经算了一天的预测销量了.
            if (i < stockUpDays - 1 && forecastPurchaseAdvisedList.get(0).compareTo(BigDecimal.ZERO) > 0) {
                stockUpSales = stockUpSales.add(forecastSalesList.get(i + 1 + turnoverDays));
            }
        }

        /*
         * dcAutoTurnover这个表单独给指定平台加上三天平均销量
         */
        calculatePlatformAvgSales(dcAutoTurnover);
        dcAutoTurnover.setSuppliersName(turnoverSku.getSuppliersName());
        dcAutoTurnover.setBuyerName(turnoverSku.getBuyerName());
        dcAutoTurnover.setBailunCategoryId(turnoverSku.getBailunCategoryId());
        dcAutoTurnover.setProductCode(turnoverSku.getProductCode());
        dcAutoTurnover.setProductInnerCode(turnoverSku.getProductInnerCode());
        dcAutoTurnover.setPaymentBeforeDelivery(paymentBeforeDelivery);
        dcAutoTurnover.setPurchaseCreateOrderDays(purchaseCreateOrderDays);
        dcAutoTurnover.setStockUpDays(stockUpDays);
        dcAutoTurnover.setStockUpSales(stockUpSales);
        dcAutoTurnover.setQuantityBeginAdvise(forecastPurchaseAdvisedList.get(0));
        if (turnoverDays == 0) {
            dcAutoTurnover.setForecastTurnoverdaySales(avgForecastTurnoverDaySales);
        } else {
            dcAutoTurnover.setForecastTurnoverdaySales(avgForecastTurnoverDaySales.divide(BigDecimal.valueOf(turnoverDays), 3, RoundingMode.HALF_EVEN));
        }
        dcAutoTurnover.setSalesExplainDetails(forecastSalesExplainList.toString());
        dcAutoTurnover.setHistorySevendaySales(avgHistorySevendaySales);
        dcAutoTurnover.setHistoryFourteendaySales(avgHistoryFourteendaySales);
        dcAutoTurnover.setHistoryThirtydaySales(avgHistoryThirtydaySales);
        dcAutoTurnover.setSupplementarySales(forecastSalesList.get(turnoverDays));
        dcAutoTurnover.setAverageSupplierDelivery(dcAutoTurnover.getSupplierDelivery());
        dcAutoTurnover.setSalesUpperLimit(salesUpperLimit);
        dcAutoTurnover.setAverageOverdue(BigDecimal.ZERO);
        dcAutoTurnover.setAdvisedDetails(moqPurchaseAdvisedList.toString());
        dcAutoTurnover.setSalesDetails(forecastSalesList.toString());
        dcAutoTurnover.setInboundDetails(forecastInboundList.toString());
        dcAutoTurnover.setInventoryDetails(forecastInventoryList.toString());
        dcAutoTurnover.setShortSupplyDetails(forecastShortSupplyList.toString());
        dcAutoTurnover.setForecastOnedaySales(avgForecastOnedaySales.compareTo(BigDecimal.ZERO) > 0 ? avgForecastOnedaySales.setScale(3, RoundingMode.HALF_EVEN) : BigDecimal.ZERO);
        dcAutoTurnover.setForecastSevendaySales(avgForecastSevendaySales.compareTo(BigDecimal.ZERO) > 0 ? avgForecastSevendaySales.divide(Constant.BIGDECIMAL_SEVEN, 3, RoundingMode.HALF_EVEN) : BigDecimal.ZERO);
        dcAutoTurnover.setForecastFourteendaySales(avgForecastFourteendaySales.compareTo(BigDecimal.ZERO) > 0 ? avgForecastFourteendaySales.divide(Constant.BIGDECIMAL_FOURTEEN, 3, RoundingMode.HALF_EVEN) : BigDecimal.ZERO);
        dcAutoTurnover.setQuantityInbound(quantityInbound);
        dcAutoTurnover.setInventoryAvailableDays(inventoryAvailableDays);
        dcAutoTurnover.setSkuTitle(turnoverSku.getSkuTitleCn());
        dcAutoTurnover.setPromotions(promotionsBuilder.toString());
        if ((warehouseCode.equals(Constant.WAREHOUSE_JIT) && (turnoverSku.getBuyerName().equals(Constant.BUYER_JIT_1) || turnoverSku.getBuyerName().equals(Constant.BUYER_JIT_2))) || (warehouseCode.equals(Constant.WAREHOUSE_JIT1) && (turnoverSku.getBuyerName().equals(Constant.BUTER_JIT_5)))) {
            dcAutoTurnover.setQuantityInitAdvise(forecastPurchaseAdvisedList.get(0));
        } else {
            dcAutoTurnover.setQuantityInitAdvise(forecastPurchaseAdvisedList.get(0).add(stockUpSales));
        }
        //如果初始建议数 + 备货数 > moq建议数, 那么moq建议数 + 备货数, 否则, 还是取moq建议数
        BigDecimal subtract = dcAutoTurnover.getQuantityInitAdvise().subtract(moqPurchaseAdvisedList.get(0));
        dcAutoTurnover.setQuantityFinalAdvise(moqPurchaseAdvisedList.get(0));
        if (subtract.compareTo(BigDecimal.ZERO) > 0) {
            dcAutoTurnover.setQuantityFinalAdvise(moqPurchaseAdvisedList.get(0).add(subtract));
        }
        dcAutoTurnover.setQuantityMinimumOrder(turnoverSku.getMoq());
        dcAutoTurnover.setQuantityInventory(realInventory);
        dcAutoTurnover.setQuantityPurchase(purchase);
        dcAutoTurnover.setQuantityTransfer(transfer);
        dcAutoTurnover.setQuantityOutStock(outStock);
        dcAutoTurnover.setStatus((byte) (forecastShortSupplyList.get(2 * turnoverDays).intValue() > 0 ? 1 : 0));
        dcAutoTurnover.setTurnoverDays(turnoverDays);
        dcAutoTurnover.setQuantitySafeInventory(totalSafeInventory);
        dcAutoTurnover.setHqType(dcBaseWarehouse.getHqType());
        dcAutoTurnover.setSalesDetailsFormula(new Gson().toJson(forecastSalesListFormula));


        try {
            DcAutoTurnoverMapper autoTurnoverMapper = SessionUtil.getSession().getMapper(DcAutoTurnoverMapper.class);

            /*
             * 增加是否断货计算
             * Add by Allan at 2020-07-22 15:47
             * 断货=前一天还有库存，但是今天库存为0了则是已经断货
             * */
            dcAutoTurnover.setIsOutStock(0);
            dcAutoTurnover.setGmtModified(LocalDateTime.now());

            if (Constant.OCEAN_HEAD.equals(transferStrategy.get())) {
                DcAutoTurnoverOceanWithBLOBs dcAutoTurnoverOceanWithBLOBs = new DcAutoTurnoverOceanWithBLOBs();
                BeanUtils.copyProperties(dcAutoTurnover, dcAutoTurnoverOceanWithBLOBs);

                DcAutoTurnoverOceanMapper dcAutoTurnoverOceanMapper = SessionUtil.getSession().getMapper(DcAutoTurnoverOceanMapper.class);
                int i = dcAutoTurnoverOceanMapper.updateByExampleSelective(dcAutoTurnoverOceanWithBLOBs, DcAutoTurnoverOceanExample.newAndCreateCriteria().andBailunSkuEqualTo(dcAutoTurnover.getBailunSku()).andWarehouseCodeEqualTo(dcAutoTurnover.getWarehouseCode()).example());
                if (i == 0) {
                    dcAutoTurnoverOceanMapper.insertSelective(dcAutoTurnoverOceanWithBLOBs);
                    this.autoTurnoverFromStock(dcBaseStock);
                }
            } else if (Constant.AIR_HEAD.equals(transferStrategy.get())) {
                DcAutoTurnoverAirWithBLOBs dcAutoTurnoverAirWithBLOBs = new DcAutoTurnoverAirWithBLOBs();
                BeanUtils.copyProperties(dcAutoTurnover, dcAutoTurnoverAirWithBLOBs);

                DcAutoTurnoverAirMapper dcAutoTurnoverAirMapper = SessionUtil.getSession().getMapper(DcAutoTurnoverAirMapper.class);
                int i = dcAutoTurnoverAirMapper.updateByExampleSelective(dcAutoTurnoverAirWithBLOBs, DcAutoTurnoverAirExample.newAndCreateCriteria().andBailunSkuEqualTo(dcAutoTurnover.getBailunSku()).andWarehouseCodeEqualTo(dcAutoTurnover.getWarehouseCode()).example());
                if (i == 0) {
                    dcAutoTurnoverAirMapper.insertSelective(dcAutoTurnoverAirWithBLOBs);
                    this.autoTurnoverFromStock(dcBaseStock);
                }
            } else {
                int i = autoTurnoverMapper.updateByExampleSelective(dcAutoTurnover, DcAutoTurnoverExample.newAndCreateCriteria().andBailunSkuEqualTo(dcAutoTurnover.getBailunSku()).andWarehouseCodeEqualTo(dcAutoTurnover.getWarehouseCode()).example());
                if (i == 0) {
                    autoTurnoverMapper.insertSelective(dcAutoTurnover);
                    this.autoTurnoverFromStock(dcBaseStock);
                }
            }


        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Mybatis操作DB失败", e);
        } finally {
            SessionUtil.closeSession();
        }

    }


    private void calculatePlatformAvgSales(DcAutoTurnoverWithBLOBs dcAutoTurnover) {
        try {
            DcAutoSalesMapper mapper = SessionUtil.getSession().getMapper(DcAutoSalesMapper.class);
            SalesVolumeAvgDTO ebayAvgSales = mapper.getPlatformHistorySales(dcAutoTurnover.getBailunSku(), dcAutoTurnover.getWarehouseCode(), PlatformType.Ebay.value());
            dcAutoTurnover.setHistorySevendaySalesEbay(BigDecimal.ZERO);
            dcAutoTurnover.setHistoryFourteendaySalesEbay(BigDecimal.ZERO);
            dcAutoTurnover.setHistoryThirtydaySalesEbay(BigDecimal.ZERO);
            if (ebayAvgSales != null) {
                dcAutoTurnover.setHistorySevendaySalesEbay(BigDecimal.valueOf(ebayAvgSales.getSevendaySales()).divide(Constant.BIGDECIMAL_SEVEN, 3, RoundingMode.HALF_EVEN));
                dcAutoTurnover.setHistoryFourteendaySalesEbay(BigDecimal.valueOf(ebayAvgSales.getFourteendaySales()).divide(Constant.BIGDECIMAL_FOURTEEN, 3, RoundingMode.HALF_EVEN));
                dcAutoTurnover.setHistoryThirtydaySalesEbay(BigDecimal.valueOf(ebayAvgSales.getThirtydaySales()).divide(Constant.BIGDECIMAL_THIRTY, 3, RoundingMode.HALF_EVEN));
            }

            mapper = SessionUtil.getSession().getMapper(DcAutoSalesMapper.class);
            SalesVolumeAvgDTO aliexpressAvgSales = mapper.getPlatformHistorySales(dcAutoTurnover.getBailunSku(), dcAutoTurnover.getWarehouseCode(), PlatformType.Aliexpress.value());
            dcAutoTurnover.setHistorySevendaySalesAliexpress(BigDecimal.ZERO);
            dcAutoTurnover.setHistoryFourteendaySalesAliexpress(BigDecimal.ZERO);
            dcAutoTurnover.setHistoryThirtydaySalesAliexpress(BigDecimal.ZERO);
            if (aliexpressAvgSales != null) {
                dcAutoTurnover.setHistorySevendaySalesAliexpress(BigDecimal.valueOf(aliexpressAvgSales.getSevendaySales()).divide(Constant.BIGDECIMAL_SEVEN, 3, RoundingMode.HALF_EVEN));
                dcAutoTurnover.setHistoryFourteendaySalesAliexpress(BigDecimal.valueOf(aliexpressAvgSales.getFourteendaySales()).divide(Constant.BIGDECIMAL_FOURTEEN, 3, RoundingMode.HALF_EVEN));
                dcAutoTurnover.setHistoryThirtydaySalesAliexpress(BigDecimal.valueOf(aliexpressAvgSales.getThirtydaySales()).divide(Constant.BIGDECIMAL_THIRTY, 3, RoundingMode.HALF_EVEN));
            }

            mapper = SessionUtil.getSession().getMapper(DcAutoSalesMapper.class);
            SalesVolumeAvgDTO amazonAvgSales = mapper.getPlatformHistorySales(dcAutoTurnover.getBailunSku(), dcAutoTurnover.getWarehouseCode(), PlatformType.Amazon.value());
            dcAutoTurnover.setHistorySevendaySalesAmazon(BigDecimal.ZERO);
            dcAutoTurnover.setHistoryFourteendaySalesAmazon(BigDecimal.ZERO);
            dcAutoTurnover.setHistoryThirtydaySalesAmazon(BigDecimal.ZERO);
            if (amazonAvgSales != null) {
                dcAutoTurnover.setHistorySevendaySalesAmazon(BigDecimal.valueOf(amazonAvgSales.getSevendaySales()).divide(Constant.BIGDECIMAL_SEVEN, 3, RoundingMode.HALF_EVEN));
                dcAutoTurnover.setHistoryFourteendaySalesAmazon(BigDecimal.valueOf(amazonAvgSales.getFourteendaySales()).divide(Constant.BIGDECIMAL_FOURTEEN, 3, RoundingMode.HALF_EVEN));
                dcAutoTurnover.setHistoryThirtydaySalesAmazon(BigDecimal.valueOf(amazonAvgSales.getThirtydaySales()).divide(Constant.BIGDECIMAL_THIRTY, 3, RoundingMode.HALF_EVEN));
            }
        } catch (Exception e) {
            throw new RuntimeException("计算平台的平均销量失败", e);
        } finally {
            SessionUtil.closeSession();
        }

    }

    private Integer getStockUpDays(String warehouseCode) {
        DcAutoConfigStockUpDaysMapper mapper = SessionUtil.getSession().getMapper(DcAutoConfigStockUpDaysMapper.class);
        DcAutoConfigStockUpDays dcAutoConfigStockUpDays = mapper.selectOneByExample(DcAutoConfigStockUpDaysExample.newAndCreateCriteria().andWarehouseCodeEqualTo(warehouseCode).example());
        if (dcAutoConfigStockUpDays != null && dcAutoConfigStockUpDays.getStatus() == 1) {
            return dcAutoConfigStockUpDays.getStockUpDays();//备货天数减去1, 因为我们已经考虑到了一天的预计销量.
        }
        return 0;
    }

    private DcAutoConfigSkuWarehouse getAutoConfigSkuWarehouse(String bailunSku, String warehouseCode) {
        try {
            DcAutoConfigSkuWarehouseMapper autoConfigSkuWarehouseMapper = SessionUtil.getSession().getMapper(DcAutoConfigSkuWarehouseMapper.class);
            return autoConfigSkuWarehouseMapper.selectOneByExample(DcAutoConfigSkuWarehouseExample.newAndCreateCriteria().andBailunSkuEqualTo(bailunSku).andWarehouseCodeEqualTo(warehouseCode).example());
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Mybatis操作DB失败", e);
        } finally {
            SessionUtil.closeSession();
        }
    }

    private boolean actualForecastFluctuations(DcAutoTurnoverWithBLOBs dcAutoTurnover,
                                               List<BigDecimal> forecastSalesList,
                                               Integer realInventory,
                                               BigDecimal totalSafeInventory,
                                               DcAutoConfigSkuWarehouse dcAutoConfigSkuWarehouse,
                                               BigDecimal avgHistoryFourteenSales) {

        //默认是停止了监控波动功能的
        boolean hasMonitorFluctuation = false;
        /*
         * 这个if判断单纯是为了判断实际冗余,然后存一个表给别人用
         */
        if (realInventory > forecastSalesList.get(0).add(totalSafeInventory).doubleValue()) {
            DcAutoDailyRedundance dcAutoDailyRedundance = new DcAutoDailyRedundance();
            float v = realInventory - forecastSalesList.get(0).add(totalSafeInventory).floatValue();
            dcAutoDailyRedundance.setQuantity(Math.round(v));
            dcAutoDailyRedundance.setBailunSku(dcAutoTurnover.getBailunSku());
            dcAutoDailyRedundance.setWarehouseCode(dcAutoTurnover.getWarehouseCode());
            dcAutoDailyRedundance.setWarehouseName(dcAutoTurnover.getWarehouseName());
            dcAutoDailyRedundance.setRecordTime(LocalDate.now().minusDays(1));
            try {
                DcAutoDailyRedundanceMapper mapper = SessionUtil.getSession().getMapper(DcAutoDailyRedundanceMapper.class);
                int i = mapper.updateByExampleSelective(dcAutoDailyRedundance, DcAutoDailyRedundanceExample.newAndCreateCriteria().andBailunSkuEqualTo(dcAutoDailyRedundance.getBailunSku()).andWarehouseCodeEqualTo(dcAutoDailyRedundance.getWarehouseCode()).andRecordTimeEqualTo(dcAutoDailyRedundance.getRecordTime()).example());
                if (i == 0) {
                    mapper.insertSelective(dcAutoDailyRedundance);
                }
            } catch (Exception e) {
                throw new RuntimeException("MYBATIS操作dcAutoDailyRedundance失败", e);
            } finally {
                SessionUtil.closeSession();
            }
        }
        /*
         * 如果没有配置停止监控
         * 库存大于0或者平均销量大于0
         * 那就要监控波动
         */
        if ((dcAutoConfigSkuWarehouse == null && (realInventory > 0 || avgHistoryFourteenSales.compareTo(BigDecimal.ZERO) > 0)) || (dcAutoConfigSkuWarehouse != null && dcAutoConfigSkuWarehouse.getStatus() == 0 && (realInventory > 0 || avgHistoryFourteenSales.compareTo(BigDecimal.ZERO) > 0))) {
            hasMonitorFluctuation = true;
        }
        return hasMonitorFluctuation;
    }

    /**
     * <p>
     * 不正常的波动
     * 实际缺货的，预计缺货
     * 实际冗余的，预计冗余(库存> 销量 + 安全库存)
     * 波动提醒优先级 -->  实际缺货-实际断货-实际冗余-预计缺货-预计冗余
     * </p>
     */
    private void unusualFluctuation(Integer turnoverDays,
                                    LocalDate recordTime,
                                    DcAutoConfigSkuWarehouse dcAutoConfigSkuWarehouse,
                                    DcBaseSku turnoverSku,
                                    String warehouseCode,
                                    DcAutoTurnoverWithBLOBs dcAutoTurnover,
                                    BigDecimal avgHistoryThreedaySales,
                                    List<DcAutoForecastFluctuation> forecastFluctuationList,
                                    List<BigDecimal> forecastShortSupplyList,
                                    DcBaseStock dcBaseStock) {

        //连续冗余或缺货天数
        int days = 0;
        //冗余或缺货天数索引
        int index = -1;

        //
        for (int i = 0; i <= turnoverDays; i++) {
            if (forecastShortSupplyList.get(i).setScale(0, RoundingMode.HALF_UP).compareTo(BigDecimal.ZERO) > 0) {
                days++;
                if (index < 0) {
                    index = i;
                }
                if (i + 1 == turnoverDays) {  //如果是最后一天
                    handleForecastFluctuation(2, forecastFluctuationList, recordTime, days, index);
                    days = 0;
                    index = -1;
                }
            } else if (index >= 0) {
                handleForecastFluctuation(2, forecastFluctuationList, recordTime, days, index);
                break;
            }
        }


        //status`  '监控状态, 默认0监控, 停止监控1',
        try {
            DcAutoForecastFluctuationMapper mapper = SessionUtil.getSession().getMapper(DcAutoForecastFluctuationMapper.class);
            //JIT的 可以不做 冗余 和 缺货预测 实际冗余和实际缺货也不用提醒
            //如果仓库是广州01 且 是这三个销售员, 就不提醒
            if (!warehouseCode.equals(Constant.WAREHOUSE_JIT)
                    || !turnoverSku.getBuyerName().equals(Constant.BUYER_JIT_1)
                    && !turnoverSku.getBuyerName().equals(Constant.BUYER_JIT_2)
                    && !turnoverSku.getBuyerName().equals(Constant.BUYER_JIT_3)) {
                if (dcAutoConfigSkuWarehouse == null || dcAutoConfigSkuWarehouse.getStatus().equals(0)) {
                    if (forecastFluctuationList != null && forecastFluctuationList.size() > 0) {

                        //拿最早的时间  +  最晚的时间, SKU-仓库维度只放一条
                        DcAutoForecastFluctuation dcAutoForecastFluctuation = forecastFluctuationList.get(0);
                        dcAutoForecastFluctuation.setStartTime(forecastFluctuationList.get(0).getStartTime());
                        dcAutoForecastFluctuation.setEndTime(forecastFluctuationList.get(0).getEndTime());
                        dcAutoForecastFluctuation.setBailunSku(turnoverSku.getBailunSku());
                        dcAutoForecastFluctuation.setWarehouseCode(warehouseCode);
                        dcAutoForecastFluctuation.setWarehouseName(dcAutoTurnover.getWarehouseName());
                        dcAutoForecastFluctuation.setAvgSalesThree(avgHistoryThreedaySales);

                        /*
                         * 增加是否断货计算
                         * Add by Allan at 2020-07-23 10:58
                         * 断货=前一天还有库存，但是今天库存为0了则是已经断货
                         * */
                        if (forecastFluctuationList.get(0).getType() != 1 && dcBaseStock.getUsableStock() == 0) {
                            //获取前一天库存
                            Integer yesterdayStock = mapper.yesterdayStock(dcAutoTurnover.getBailunSku(), dcAutoTurnover.getWarehouseCode());
                            if (yesterdayStock != null && yesterdayStock > 0) {
                                dcAutoForecastFluctuation.setType(5); //实际断货
                                dcAutoForecastFluctuation.setStartTime(LocalDate.now());  //保存当天日期
                                dcAutoForecastFluctuation.setEndTime(LocalDate.now());    //保存当天日期
                            }
                        }

                        //End Add

                        int i = mapper.updateByExampleSelective(dcAutoForecastFluctuation, DcAutoForecastFluctuationExample.newAndCreateCriteria().andBailunSkuEqualTo(dcAutoForecastFluctuation.getBailunSku()).andWarehouseCodeEqualTo(dcAutoForecastFluctuation.getWarehouseCode()).example());
                        if (i == 0) {
                            mapper.insertSelective(dcAutoForecastFluctuation);
                        }
                    } else {
                        mapper.deleteByExample(DcAutoForecastFluctuationExample.newAndCreateCriteria()
                                .andBailunSkuEqualTo(turnoverSku.getBailunSku())
                                .andWarehouseCodeEqualTo(warehouseCode).example());
                    }
                } else if (dcAutoConfigSkuWarehouse.getStatus().equals(1)) {
                    mapper.deleteByExample(DcAutoForecastFluctuationExample.newAndCreateCriteria()
                            .andBailunSkuEqualTo(turnoverSku.getBailunSku())
                            .andWarehouseCodeEqualTo(warehouseCode).example());
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("MYBATIS操作DB存DcAutoForecastFluctuation失败", e);
        } finally {
            SessionUtil.closeSession();
        }
    }


    private BigDecimal calculateWeightingAvgSales(List<BigDecimal> salesList) {
        return this.calculateWeightingAvgSales(new BigDecimal("0.4"), new BigDecimal("0.4"), new BigDecimal("0.4"), salesList);
    }

    private BigDecimal calculateWeightingAvgSales(BigDecimal weightingCoefficientSeven, BigDecimal weightingCoefficientFourteen, BigDecimal weightingCoefficientThirty, List<BigDecimal> salesList) {
        List<BigDecimal> historySevenSalesList = new ArrayList<>(7);
        List<BigDecimal> historyFourteenSalesList = new ArrayList<>(14);
        for (int i = 16; i < salesList.size(); i++) {
            historyFourteenSalesList.add(salesList.get(i));
            if (i >= 23) {
                historySevenSalesList.add(salesList.get(i));
            }
        }
        //自定义Comparator对象，自定义排序
        Comparator<BigDecimal> c = (o1, o2) -> Integer.compare(0, o1.compareTo(o2));
        salesList.sort(c);
        historyFourteenSalesList.sort(c);
        historySevenSalesList.sort(c);
        BigDecimal thirtyHistorySales = BigDecimal.ZERO;
        BigDecimal fourteenHistorySales = BigDecimal.ZERO;
        BigDecimal sevenHistorySales = BigDecimal.ZERO;
        for (int i = 1; i < salesList.size() - 1; i++) {
            thirtyHistorySales = thirtyHistorySales.add(salesList.get(i));
        }
        for (int i = 1; i < historyFourteenSalesList.size() - 1; i++) {
            fourteenHistorySales = fourteenHistorySales.add(historyFourteenSalesList.get(i));
        }
        for (int i = 1; i < historySevenSalesList.size() - 1; i++) {
            sevenHistorySales = sevenHistorySales.add(historySevenSalesList.get(i));
        }
        BigDecimal weightingSevenAvgSales = thirtyHistorySales.divide(BigDecimal.valueOf(salesList.size() - 2), 3, RoundingMode.HALF_EVEN);
        BigDecimal weightingFourteenAvgSales = fourteenHistorySales.divide(BigDecimal.valueOf(historyFourteenSalesList.size() - 2), 3, RoundingMode.HALF_EVEN);
        BigDecimal weightingThirtyAvgSales = sevenHistorySales.divide(BigDecimal.valueOf(historySevenSalesList.size() - 2), 3, RoundingMode.HALF_EVEN);
        return weightingSevenAvgSales.multiply(weightingCoefficientSeven).add(weightingFourteenAvgSales.multiply(weightingCoefficientFourteen)).add(weightingThirtyAvgSales.multiply(weightingCoefficientThirty)).setScale(3, RoundingMode.HALF_EVEN);
    }

    /**
     * 预计入库
     */
    private void ExpectedStorage(DcAutoConfigDelivery dcAutoConfigDelivery,
                                 List<String> forecastInboundRelationList,
                                 Integer purchase,
                                 Integer transfer,
                                 LocalDate recordTime,
                                 DcAutoTurnoverWithBLOBs dcAutoTurnover,
                                 List<Integer> forecastPurchaseInboundList,
                                 List<Integer> forecastTransferInboundList,
                                 List<Integer> forecastInboundList) {
        /*
         * 拿到预计到货时间在周转期内的采购单
         * 采购系统的预计到货只是到货时间
         * 还需要加上入库时长,
         * 且不同交通方式的入库时长不一致
         */
        //拿采购单流水
        if (purchase > 0) {
            List<DcBasePurchase> dcBasePurchaseList;
            // 找采购在途的采购单
            try {
                DcBasePurchaseMapper dcBasePurchaseMapper = SessionUtil.getSession().getMapper(DcBasePurchaseMapper.class);
                dcBasePurchaseList = dcBasePurchaseMapper.listPurchaseTransitOrder(dcAutoTurnover.getBailunSku(), dcAutoTurnover.getWarehouseCode());
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("Mybatis操作DB查询采购单流水失败");
            } finally {
                SessionUtil.closeSession();
            }

            //
            if (dcBasePurchaseList != null && dcBasePurchaseList.size() > 0) {
                for (DcBasePurchase dcBasePurchase : dcBasePurchaseList) {
                    if (dcBasePurchase.getCount() > 0) {
                        LocalDateTime finalEstimatedArrivalTime;
                        //调拨采购单
                        if (dcBasePurchase.getHasTransfer() == 1) {
                            finalEstimatedArrivalTime = dcBasePurchase.getEstimatedArrivalTime().plusDays(dcAutoTurnover.getInspectionDelivery()).plusDays(dcAutoTurnover.getTransferBaleDelivery()).plusDays(dcAutoTurnover.getTransferDelivery()).plusDays(dcAutoTurnover.getAbroadInboundDelivery());
                        }
                        //普通采购单
                        else {
                            finalEstimatedArrivalTime = dcBasePurchase.getEstimatedArrivalTime().plusDays(dcAutoTurnover.getInspectionDelivery());
                        }
//                        int interval = between(, finalEstimatedArrivalTime.toLocalDate()).getDays();
                        int interval = (int) recordTime.until(finalEstimatedArrivalTime, ChronoUnit.DAYS);
                        int index = Math.max(interval, 0);
                        forecastInboundRelationList.set(index, forecastInboundRelationList.get(index).equals(Constant.NAN_STRING) ? Constant.PURCHASE_SIGN + dcBasePurchase.getPurchaseId() + "_" + dcBasePurchase.getCount() : forecastInboundRelationList.get(index) + "*" + Constant.PURCHASE_SIGN + dcBasePurchase.getPurchaseId() + "_" + dcBasePurchase.getCount());
                        forecastPurchaseInboundList.set(index, forecastPurchaseInboundList.get(index) + dcBasePurchase.getCount());
                        forecastInboundList.set(index, forecastPurchaseInboundList.get(index) + forecastTransferInboundList.get(index));
                    }
                }
            }
        }


        //拿调拨单流水
        if (transfer > 0) {
            List<DcBaseTransferVerify> dcBaseTransferVerifyList;
            try {
                DcBaseTransferVerifyMapper dcBaseTransferVerifyMapper = SessionUtil.getSession().getMapper(DcBaseTransferVerifyMapper.class);
                // 找调拨在途的采购单
                dcBaseTransferVerifyList = dcBaseTransferVerifyMapper.listTransferTransitOrder(dcAutoTurnover.getBailunSku(), dcAutoTurnover.getWarehouseCode());
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("Mybatis操作DB查询调拨单流水失败");
            } finally {
                SessionUtil.closeSession();
            }
            if (dcBaseTransferVerifyList != null && dcBaseTransferVerifyList.size() > 0) {
                for (DcBaseTransferVerify dcBaseTransferVerify : dcBaseTransferVerifyList) {
                    if (dcBaseTransferVerify.getCount() > 0) {
                        LocalDateTime finalEstimatedArrivalTime;
                        if (dcBaseTransferVerify.getEstimatedArrivalTime() != null && dcBaseTransferVerify.getEstimatedArrivalTime().isAfter(Constant.INIT_DATE_TIME)) {
                            finalEstimatedArrivalTime = dcBaseTransferVerify.getEstimatedArrivalTime().plusDays(dcAutoTurnover.getInspectionConfigDelivery());
                        } else {
                            finalEstimatedArrivalTime = dcBaseTransferVerify.getCreateTime().plusDays(dcAutoConfigDelivery.getAbroadInbound()).plusDays(dcAutoConfigDelivery.getTranferBale()).plusDays(dcAutoConfigDelivery.getTranferHead());
                        }
                        int interval = (int) recordTime.until(finalEstimatedArrivalTime, ChronoUnit.DAYS);
                        int index = Math.max(interval, 0);

                        //todo fba的调拨在途，切换读我做的一个新表（表结构构建中）
                        try {
                            DcBaseWarehouseMapper dcBaseWarehouseMapper = SessionUtil.getSession().getMapper(DcBaseWarehouseMapper.class);
                            DcBaseWarehouse dcBaseWarehouse = dcBaseWarehouseMapper.selectOneByExample(DcBaseWarehouseExample
                                    .newAndCreateCriteria()
                                    .andWarehouseCodeEqualTo(dcAutoTurnover.getWarehouseCode())
                                    .example());
                            if (!dcBaseWarehouse.getSystemFlag().toUpperCase().equals(PlatformType.FBA.value())) {
                                forecastInboundRelationList.set(index, forecastInboundRelationList.get(index).equals(Constant.NAN_STRING) ? Constant.TRANSFER_SIGN + dcBaseTransferVerify.getTransferOrderId() + "_" + dcBaseTransferVerify.getCount() : forecastInboundRelationList.get(index) + "*" + Constant.TRANSFER_SIGN + dcBaseTransferVerify.getTransferOrderId() + "_" + dcBaseTransferVerify.getCount());
                                forecastTransferInboundList.set(index, forecastTransferInboundList.get(index) + dcBaseTransferVerify.getCount());
                                forecastInboundList.set(index, forecastPurchaseInboundList.get(index) + forecastTransferInboundList.get(index));
                            }

                        } finally {
                            SessionUtil.closeSession();
                        }

                    }
                }
            }
        }


        //fba
        try {
            DcBaseTransExpectarrivaltimeMapper dcBaseTransExpectarrivaltimeMapper = SessionUtil.getSession().getMapper(DcBaseTransExpectarrivaltimeMapper.class);
            DcBaseWarehouseMapper dcBaseWarehouseMapper = SessionUtil.getSession().getMapper(DcBaseWarehouseMapper.class);
            DcBaseWarehouse dcBaseWarehouse = dcBaseWarehouseMapper.selectOneByExample(DcBaseWarehouseExample
                    .newAndCreateCriteria()
                    .andWarehouseCodeEqualTo(dcAutoTurnover.getWarehouseCode())
                    .example());

            if (dcBaseWarehouse.getSystemFlag().toUpperCase().equals(PlatformType.FBA.value())) {

                // 找调拨在途的采购单
                List<DcBaseTransExpectarrivaltime> dcBaseTransExpectarrivaltimeList = dcBaseTransExpectarrivaltimeMapper.selectByExample(DcBaseTransExpectarrivaltimeExample
                        .newAndCreateCriteria()
                        .andBailunSkuEqualTo(dcAutoTurnover.getBailunSku())
                        .andWarehouseCodeEqualTo(dcAutoTurnover.getWarehouseCode())
                        .example());

                Map<LocalDate, List<DcBaseTransExpectarrivaltime>> map = dcBaseTransExpectarrivaltimeList.stream().collect(Collectors.groupingBy(item -> item.getExpectarrivaltime().toLocalDate()));
                map.forEach((localDate, dcBaseTransExpectarrivaltimes) -> {
                    int interval = between(recordTime, localDate).getDays();
                    int index = Math.max(interval, 0);

                    forecastInboundRelationList.set(index, forecastInboundRelationList.get(index) + "*" + dcBaseTransExpectarrivaltimes.stream().map(dcBaseTransExpectarrivaltime -> Constant.TRANSFER_SIGN + dcBaseTransExpectarrivaltime.getTransferOrderId() + "_" + dcBaseTransExpectarrivaltime.getCount()).collect(Collectors.joining("*")));
                    forecastTransferInboundList.set(index, forecastTransferInboundList.get(index) + dcBaseTransExpectarrivaltimes.stream().mapToInt(DcBaseTransExpectarrivaltime::getCount).sum());
                    forecastInboundList.set(index, forecastPurchaseInboundList.get(index) + forecastTransferInboundList.get(index));

                });
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Mybatis操作DB查询fba的调拨在途，切换读我做的一个新表（表结构构建中）");
        } finally {
            SessionUtil.closeSession();
        }
    }

    private StringBuilder promotionForecastSales(LocalDate recordTime, Integer autoForecastDay, DcAutoSales
            dcAutoSales, List<BigDecimal> forecastSalesList) {
        //周转周期里有活动,可能有多个
        int promotionCount = 0;
        StringBuilder promotionsBuilder = new StringBuilder();
        List<DcAutoConfigPromotion> autoSalesConfigList;
        try {
            DcAutoConfigPromotionMapper dcAutoConfigPromotionMapper = SessionUtil.getSession().getMapper(DcAutoConfigPromotionMapper.class);
            autoSalesConfigList = dcAutoConfigPromotionMapper.selectByExample(DcAutoConfigPromotionExample.newAndCreateCriteria().andBailunSkuEqualTo(dcAutoSales.getBailunSku()).andWarehouseCodeEqualTo(dcAutoSales.getWarehouseCode()).andPromotionTimeBetween(recordTime, recordTime.plusDays(autoForecastDay.longValue())).example());
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Mybatis操作DB查询周转周期里活动失败");
        } finally {
            SessionUtil.closeSession();
        }
        if (autoSalesConfigList != null && autoSalesConfigList.size() > 0) {
            for (DcAutoConfigPromotion dcAutoConfigPromotion : autoSalesConfigList) {
                if (promotionsBuilder.length() > 0) {
                    promotionsBuilder.append(",");
                }
                promotionsBuilder.append(dcAutoConfigPromotion.getId());

                //看促销日期和recordTime相差多少天
                int interval = between(recordTime, dcAutoConfigPromotion.getPromotionTime()).getDays();
                if (interval >= 0 && interval < autoForecastDay) {
                    promotionCount = promotionCount + dcAutoConfigPromotion.getCount();
                    forecastSalesList.set(interval, forecastSalesList.get(interval).add(BigDecimal.valueOf(dcAutoConfigPromotion.getCount())));
                }
            }
        }
        return promotionsBuilder;
    }

    /**
     * 去数据库查出矫正曲线配置
     * 用配置的参数乘以指定销量
     * dcAutoConfigCorrection.getStartTime() 要大于等于queryTime
     */
    private void correctionForecastSales(LocalDate recordTime,
                                         Integer autoForecastDay,
                                         DcAutoSales dcAutoSales,
                                         List<BigDecimal> forecastSalesList) {
        List<DcAutoConfigCorrection> configCorrectionList;
        try {
            DcAutoConfigCorrectionMapper autoConfigCorrectionMapper = SessionUtil.getSession().getMapper(DcAutoConfigCorrectionMapper.class);
            configCorrectionList = autoConfigCorrectionMapper.selectByExample(DcAutoConfigCorrectionExample.newAndCreateCriteria().andBailunSkuEqualTo(dcAutoSales.getBailunSku()).andWarehouseCodeEqualTo(dcAutoSales.getWarehouseCode()).andStartTimeGreaterThanOrEqualTo(recordTime).example());
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Mybatis操作DB查询矫正曲线配置失败");
        } finally {
            SessionUtil.closeSession();
        }
        if (configCorrectionList != null && configCorrectionList.size() > 0) {
            for (DcAutoConfigCorrection dcAutoConfigCorrection : configCorrectionList) {
                if (dcAutoConfigCorrection.getStartTime() != null && dcAutoConfigCorrection.getEndTime() == null) {
                    int interval = between(recordTime, dcAutoConfigCorrection.getStartTime()).getDays();
                    BigDecimal param = dcAutoConfigCorrection.getParam();
                    forecastSalesList.set(interval, param.multiply(forecastSalesList.get(interval)).setScale(3, RoundingMode.HALF_EVEN));
                } else if (dcAutoConfigCorrection.getStartTime() != null && dcAutoConfigCorrection.getEndTime() != null) {
                    int begin = between(recordTime, dcAutoConfigCorrection.getStartTime()).getDays();
                    int end = between(recordTime, dcAutoConfigCorrection.getEndTime()).getDays();
                    end = Math.min(end, autoForecastDay);
                    for (int i = begin; i <= end; i++) {
                        forecastSalesList.set(i, dcAutoConfigCorrection.getParam().multiply(forecastSalesList.get(i)).setScale(3, RoundingMode.HALF_EVEN));
                    }
                }
            }
        }
    }

    /**
     * 初始化容器, 存放预测数据
     *
     * @param forecastInventoryList       预测库存
     * @param forecastPurchaseInboundList 预测采购入库
     * @param forecastTransferInboundList 预测调拨入库
     * @param forecastInboundList         预测入库总数
     * @param forecastSalesList           预测销量
     * @param forecastShortSupplyList     预测断货
     * @param autoForecastDay             周转倍数
     */
    private void initForecastList(List<String> forecastInboundRelationList,
                                  List<BigDecimal> forecastInventoryList,
                                  List<Integer> forecastPurchaseInboundList,
                                  List<Integer> forecastTransferInboundList,
                                  List<Integer> forecastInboundList,
                                  List<BigDecimal> forecastSalesList,
                                  List<BigDecimal> forecastShortSupplyList,
                                  Integer autoForecastDay,
                                  List<String> forecastSalesExplainList) {
        for (int i1 = 0; i1 < autoForecastDay; i1++) {

            forecastInboundList.add(0);
            forecastInventoryList.add(BigDecimal.ZERO);
            forecastSalesList.add(BigDecimal.ZERO);
            forecastSalesExplainList.add(Constant.ALGORITHM_NAME);
            forecastShortSupplyList.add(BigDecimal.ZERO);
            forecastPurchaseInboundList.add(0);
            forecastTransferInboundList.add(0);
            forecastTransferInboundList.add(0);
            forecastInboundRelationList.add(Constant.NAN_STRING);
        }
    }


    private BigDecimal calculateSalesUpperLimit(String bailunSku,
                                                String warehouseCode,
                                                Integer bailunFirstLevelCategoryId,
                                                DcAutoSales dcAutoSales) {
        /*
         * 1. 根据仓库SKU找销量上限, 有就停止, 没有则2
         * 2. 根据仓库和分类找销量上限, 有就停止, 没有则3
         * 3. 根据加权平均算上限 * 倍数
         */
        DcAutoConfigSalesUpperLimitMapper salesUpperLimitMapper;
        DcAutoConfigSalesUpperLimit dcAutoConfigSalesUpperLimit;
        try {
            salesUpperLimitMapper = SessionUtil.getSession().getMapper(DcAutoConfigSalesUpperLimitMapper.class);
            dcAutoConfigSalesUpperLimit = salesUpperLimitMapper.selectOneByExample(DcAutoConfigSalesUpperLimitExample.newAndCreateCriteria().andVariableCodeEqualTo(bailunSku).andWarehouseCodeEqualTo(warehouseCode).andStatusEqualTo(1).andTypeEqualTo(1).example());
            if (dcAutoConfigSalesUpperLimit == null) {
                if (bailunFirstLevelCategoryId != null) {//部分SKU没有百伦分类Id
                    dcAutoConfigSalesUpperLimit = salesUpperLimitMapper.selectOneByExample(DcAutoConfigSalesUpperLimitExample.newAndCreateCriteria().andVariableCodeEqualTo(bailunFirstLevelCategoryId.toString()).andWarehouseCodeEqualTo(warehouseCode).andStatusEqualTo(1).andTypeEqualTo(2).example());
                }
            }
            if (dcAutoConfigSalesUpperLimit == null) {
                BigDecimal weightingAvgSales;
                //历史
                dcAutoConfigSalesUpperLimit = salesUpperLimitMapper.selectOneByExample(DcAutoConfigSalesUpperLimitExample.newAndCreateCriteria().andVariableCodeEqualTo(bailunSku).andWarehouseCodeEqualTo(warehouseCode).andStatusEqualTo(1).andTypeEqualTo(1).example());
                List<BigDecimal> historyThirtySalesList = JSON.parseObject(dcAutoSales.getHistorySalesDetails(), new TypeReference<List<BigDecimal>>() {
                });
                if (dcAutoConfigSalesUpperLimit != null) {
                    weightingAvgSales = calculateWeightingAvgSales(dcAutoConfigSalesUpperLimit.getWeightingCoefficientSeven(), dcAutoConfigSalesUpperLimit.getWeightingCoefficientFourteen(), dcAutoConfigSalesUpperLimit.getWeightingCoefficientThirty(), historyThirtySalesList);
                    weightingAvgSales = weightingAvgSales.multiply(dcAutoConfigSalesUpperLimit.getParam()).setScale(3, RoundingMode.HALF_EVEN);
                } else {
                    //如果没有任何配置, 就留一个默认配置
                    weightingAvgSales = calculateWeightingAvgSales(historyThirtySalesList);
                }
                return weightingAvgSales;
            } else {
                return dcAutoConfigSalesUpperLimit.getParam();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Mybatis操作DB失败", e);
        } finally {
            SessionUtil.closeSession();
        }
    }

    private BigDecimal calculateSafeInventory(DcBaseSku turnoverSku, String bailunSku, String warehouseCode, Integer
            bailunFirstLevelCategoryId, DcAutoSales dcAutoSales, String forecastSalesListJson, DcAutoTurnoverWithBLOBs dcAutoTurnover, Map<String, BigDecimal> saveDaysMap) {
        /*
         *  先判断是否JIT  仓库是广州01 warehouse_code 且是那三个销售员 turnoversku
         *
         */
        DcAutoStockUpRangeMapper dcAutoStockUpRangeMapper = SessionUtil.getSession().getMapper(DcAutoStockUpRangeMapper.class);
        DcAutoStockUpRange dcAutoStockUpRange = dcAutoStockUpRangeMapper.selectOneByExample(DcAutoStockUpRangeExample.newAndCreateCriteria().andBailunSkuEqualTo(bailunSku).andWarehouseCodeEqualTo(warehouseCode).example());
        if ((warehouseCode.equals(Constant.WAREHOUSE_JIT) && (turnoverSku.getBuyerName().equals(Constant.BUYER_JIT_1) || turnoverSku.getBuyerName().equals(Constant.BUYER_JIT_2) || turnoverSku.getBuyerName().equals(Constant.BUYER_JIT_3))) || dcAutoStockUpRange != null || (warehouseCode.equals(Constant.WAREHOUSE_JIT1) && (turnoverSku.getBuyerName().equals(Constant.BUTER_JIT_5)))) {
            List<BigDecimal> historyThirtySalesList = JSON.parseObject(dcAutoSales.getHistorySalesDetails(), new TypeReference<List<BigDecimal>>() {
            });
            BigDecimal weightingAvgSales = calculateWeightingAvgSales(historyThirtySalesList);
            if (dcAutoStockUpRange == null && (warehouseCode.equals(Constant.WAREHOUSE_JIT1) && (turnoverSku.getBuyerName().equals(Constant.BUTER_JIT_5)))) {
                dcAutoStockUpRange = new DcAutoStockUpRange();
                dcAutoStockUpRange.setBailunSku(bailunSku);
                dcAutoStockUpRange.setGroupId(2);
                dcAutoStockUpRange.setWarehouseCode(warehouseCode);
                dcAutoStockUpRange.setWarehouseName("百伦【广州-03仓】");
                dcAutoStockUpRangeMapper.insertSelective(dcAutoStockUpRange);
            }
            BigDecimal saveDays = getSaveDays(dcAutoSales, dcAutoTurnover, bailunSku, turnoverSku.getBuyerName(), "weightingAvgSales", dcAutoStockUpRange);
            saveDaysMap.put("saveDays", saveDays);
            dcAutoTurnover.setDailyWeightedSales(weightingAvgSales);
            weightingAvgSales = weightingAvgSales.multiply(saveDays);
            return weightingAvgSales;
        } else {
            /*
             * 手动设置
             * 1. 根据仓库 + SKU在表里找dc_auto_config_safe_inventory
             * 2. 根据仓库 + 类目在表里找dc_auto_config_safe_inventory
             * 当手动设置的值不为null, 就返回;
             * 当手动设置的值为null, 就自动计算加权平均销量
             *
             * 自动计算
             * 计算7天平均销量14天平均销量30天平均销量
             * 1. 先找按预测计算是否存在表
             *   a. 有就取出权重
             *   b. 没有就找按历史计算是否存在表
             * 2. 找按历史计算是否存在表
             *   a. 有就取出权重
             *   b. 没有就将7天平均销量14天平均销量30天平均销量再平均
             *
             * 最终
             * 平均交期 - 供应商交期 > 0  就是平均逾期天数
             * 平均逾期天数 * 加权平均销量(去最大值最小值)
             *
             * 新增一种算法（优先级别最低的），直接配置仓库。根据历史销量加权计算。最后可以乘以一个倍数
             */
            DcAutoConfigSafeInventoryMapper autoConfigSafeInventoryMapper;
            DcAutoConfigSafeInventory dcAutoConfigSafeInventory;
            try {
                autoConfigSafeInventoryMapper = SessionUtil.getSession().getMapper(DcAutoConfigSafeInventoryMapper.class);
                dcAutoConfigSafeInventory = autoConfigSafeInventoryMapper.selectOneByExample(DcAutoConfigSafeInventoryExample.newAndCreateCriteria().andVariableCodeEqualTo(bailunSku).andWarehouseCodeEqualTo(warehouseCode).andStatusEqualTo(1).andTypeEqualTo(1).example());

                if (dcAutoConfigSafeInventory == null) {
                    if (bailunFirstLevelCategoryId != null) {//部分SKU没有百伦分类Id
                        dcAutoConfigSafeInventory = autoConfigSafeInventoryMapper.selectOneByExample(DcAutoConfigSafeInventoryExample.newAndCreateCriteria().andVariableCodeEqualTo(bailunFirstLevelCategoryId.toString()).andWarehouseCodeEqualTo(warehouseCode).andStatusEqualTo(1).andTypeEqualTo(2).example());
                    }
                }
                if (dcAutoConfigSafeInventory == null) {
                    BigDecimal weightingAvgSales;
                    //预测
                    dcAutoConfigSafeInventory = autoConfigSafeInventoryMapper.selectOneByExample(DcAutoConfigSafeInventoryExample.newAndCreateCriteria().andVariableCodeEqualTo(bailunSku).andWarehouseCodeEqualTo(warehouseCode).andStatusEqualTo(1).andTypeEqualTo(12).example());
                    if (dcAutoConfigSafeInventory != null) {
                        List<BigDecimal> forecastSalesList = JSON.parseObject(forecastSalesListJson, new TypeReference<List<BigDecimal>>() {
                        });
                        forecastSalesList.remove(forecastSalesList.size() - 1);
                        weightingAvgSales = calculateWeightingAvgSales(dcAutoConfigSafeInventory.getWeightingCoefficientSeven(), dcAutoConfigSafeInventory.getWeightingCoefficientFourteen(), dcAutoConfigSafeInventory.getWeightingCoefficientThirty(), forecastSalesList);
                    } else {
                        //历史
                        dcAutoConfigSafeInventory = autoConfigSafeInventoryMapper.selectOneByExample(DcAutoConfigSafeInventoryExample.newAndCreateCriteria().andVariableCodeEqualTo(bailunSku).andWarehouseCodeEqualTo(warehouseCode).andStatusEqualTo(1).andTypeEqualTo(11).example());
                        List<BigDecimal> historyThirtySalesList = JSON.parseObject(dcAutoSales.getHistorySalesDetails(), new TypeReference<List<BigDecimal>>() {
                        });
                        if (dcAutoConfigSafeInventory != null) {
                            weightingAvgSales = calculateWeightingAvgSales(dcAutoConfigSafeInventory.getWeightingCoefficientSeven(), dcAutoConfigSafeInventory.getWeightingCoefficientFourteen(), dcAutoConfigSafeInventory.getWeightingCoefficientThirty(), historyThirtySalesList);
                        } else {
                            //仓库。根据历史销量加权计算。最后可以乘以一个倍数
                            dcAutoConfigSafeInventory = autoConfigSafeInventoryMapper.selectOneByExample(DcAutoConfigSafeInventoryExample.newAndCreateCriteria().andVariableCodeEqualTo("").andWarehouseCodeEqualTo(warehouseCode).andStatusEqualTo(1).andTypeEqualTo(3).example());
                            if (dcAutoConfigSafeInventory != null) {

                                weightingAvgSales = calculateWeightingAvgSales(dcAutoConfigSafeInventory.getWeightingCoefficientSeven(), dcAutoConfigSafeInventory.getWeightingCoefficientFourteen(), dcAutoConfigSafeInventory.getWeightingCoefficientThirty(), historyThirtySalesList);
                                dcAutoTurnover.setDailyWeightedSales(weightingAvgSales);
                                //乘倍数后的加权日均销量,小于0.8就置0   0.8-1 之间就置1
                                weightingAvgSales = weightingAvgSales.multiply(dcAutoConfigSafeInventory.getParam()).setScale(3, RoundingMode.HALF_EVEN);
                                if (weightingAvgSales.compareTo(BigDecimal.valueOf(0.8)) < 0) {
                                    weightingAvgSales = BigDecimal.ZERO;
                                } else if (weightingAvgSales.compareTo(BigDecimal.ONE) <= 0) {
                                    weightingAvgSales = BigDecimal.ONE;
                                }
                            } else {
                                //啥配置都没有就给个默认取值
                                weightingAvgSales = calculateWeightingAvgSales(historyThirtySalesList);
                                dcAutoTurnover.setDailyWeightedSales(weightingAvgSales);
                            }
                        }
                    }
                    return weightingAvgSales;
                } else {
                    return dcAutoConfigSafeInventory.getParam();
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("Mybatis操作DB失败", e);
            } finally {
                SessionUtil.closeSession();
            }
        }
    }


    private Integer fbaAdvise(DcBaseSku turnoverSku,
                              String warehouseCode,
                              DcAutoTurnoverWithBLOBs dcAutoTurnover,
                              Integer turnoverDays,
                              Integer autoForecastDay,
                              List<BigDecimal> forecastInventoryList,
                              List<Integer> forecastInboundList,
                              List<BigDecimal> forecastSalesList,
                              List<SalesDetailItem> salesDetailItemList,
                              List<BigDecimal> forecastShortSupplyList,
                              List<BigDecimal> forecastPurchaseAdvisedList,
                              List<BigDecimal> moqPurchaseAdvisedList,
                              Integer realInventory,
                              BigDecimal totalSafeInventory,
                              Integer outStock,
                              Integer inventoryAvailableDays,
                              BigDecimal totalSales,
                              BigDecimal totalInbound,
                              BigDecimal moqDecimal,
                              List<Integer> historySalesList, List<String> forecastSalesExplainList) {

        BigDecimal inboundDecimal;//入库数量
        BigDecimal forecastInventory;//预测库存
        BigDecimal shortSupply;//缺货数量
        BigDecimal addShortSupply;// 补断货的数量
        int shortSupplyDays = 0;//断货天数
        int shortSupplyIndex = -1;//断货天索引

        int fbaTransferIndex = 1;

        for (int j = 0; j < autoForecastDay; j++) {
            BigDecimal forecastSalesItem = forecastSalesList.get(j);
            inboundDecimal = BigDecimal.valueOf(forecastInboundList.get(j));
            totalInbound = totalInbound.add(inboundDecimal);
            if (j == turnoverDays) {
                dcAutoTurnover.setTurnoverInbound(totalInbound);
            }

            if (j == 0) { //j == 0表示, 周转计算的当天
                if (outStock > 0) {
                    forecastSalesList.set(j, BigDecimal.ZERO);
                    forecastSalesExplainList.set(j, "0, 昨天真实缺货");
                    shortSupplyIndex = j;
                    //如果有缺货数, 第一天预计库存 =  预计入库 - 缺货数
                    forecastInventory = BigDecimal.valueOf((forecastInboundList.get(j).doubleValue() - outStock.doubleValue()));
                    shortSupply = forecastInventory.compareTo(BigDecimal.ZERO) < 0 ? forecastInventory.abs() : BigDecimal.ZERO;//库存补入后的 缺货数
                    BigDecimal subtract = forecastSalesList.get(j).subtract(forecastInventory.compareTo(BigDecimal.ZERO) > 0 ? forecastInventory : BigDecimal.ZERO);//今天的缺货数
                    shortSupply = subtract.add(shortSupply).compareTo(BigDecimal.ZERO) > 0 ? subtract.add(shortSupply) : BigDecimal.ZERO;
                } else {
                    //否则, 第一天预计库存 =  真实库存 + 预计入库
                    forecastInventory = BigDecimal.valueOf((forecastInboundList.get(j).doubleValue() + realInventory.doubleValue()));
                    BigDecimal subtract = forecastSalesList.get(j).subtract(forecastInventory);
                    shortSupply = subtract.compareTo(BigDecimal.ZERO) > 0 ? subtract : BigDecimal.ZERO;
                }
                forecastShortSupplyList.set(j, forecastShortSupplyList.get(j).add(shortSupply));
            } else { //j != 0表示, 周转计算之后的天数
                if (forecastShortSupplyList.get(j - 1).compareTo(BigDecimal.ZERO) > 0) {//是断货
                    forecastSalesList.set(j, BigDecimal.ZERO);
                    forecastSalesExplainList.set(j, "昨天预测缺货");
                    if (shortSupplyIndex < 0) {
                        shortSupplyIndex = j;
                    }
                    try {
                        DcAutoFastestStorageMapper dcAutoFastestStorageMapper = SessionUtil.getSession().getMapper(DcAutoFastestStorageMapper.class);
                        DcAutoFastestStorage dcAutoFastestStorage = dcAutoFastestStorageMapper.selectOneByExample(DcAutoFastestStorageExample.newAndCreateCriteria()
                                .andBailunSkuEqualTo(dcAutoTurnover.getBailunSku())
                                .andWarehouseCodeEqualTo(dcAutoTurnover.getWarehouseCode())
                                .example());

                        //日均销量  * （1 - （N * 0.3）+ （X * 0.1）））
                        if (dcAutoFastestStorage != null) {
                            int n = dcAutoFastestStorage.getPurchaseDays() + dcAutoFastestStorage.getTransDays();
                            int x = Math.max(fbaTransferIndex - n, 0);
                            BigDecimal multiSales = BigDecimal.valueOf(Math.min(1, Math.max(1 - n * 0.3, 0) + x * 0.1));

                            if (x >= 1) {
                                forecastSalesList.set(j, forecastSalesItem.multiply(multiSales));
                            }

                            if (salesDetailItemList.get(j) != null && salesDetailItemList.get(j).getSalesDayConfig() != null) {
                                forecastSalesList.set(j, salesDetailItemList.get(j).getSalesDayConfig());
                            }
                        }

                    } finally {
                        SessionUtil.closeSession();
                    }
                    shortSupplyDays++;
                    fbaTransferIndex++;
                } else {
                    if (shortSupplyIndex == 0) {
                        //尽量降序找一个不为0的历史销量
                        for (int i = historySalesList.size() - 1; i >= 0; i--) {
                            if (historySalesList.get(i) > 0) {
                                break;
                            }
                        }

                        shortSupplyIndex = -1;
                        shortSupplyDays = 0;
                    } else if (shortSupplyIndex > 0) {
                        //尽量降序找一个不为0的预测销量
                        BigDecimal forecastSaleGTZero = null;
                        for (int i = shortSupplyIndex; i >= 0; i--) {
                            forecastSaleGTZero = BigDecimal.ZERO;
                            if (forecastSalesList.get(i).compareTo(BigDecimal.ZERO) > 0) {
                                forecastSaleGTZero = forecastSalesList.get(i);
                                break;
                            }
                        }
                        //按照规则递减或递增 j = 63
                        for (int i = 0; i <= shortSupplyDays; i++) {
                            BigDecimal result = forecastSaleGTZero
                                    .multiply(Constant.FBA_OUT_STOCK_DECREASE
                                            .pow(shortSupplyDays))
                                    .multiply(Constant.FBA_OUT_STOCK_INCREMENT
                                            .pow(i))
                                    .setScale(3, RoundingMode.HALF_EVEN);
                            //索引越界
                            if (j + i < forecastSalesList.size()) {
                                forecastSalesList.set(j + i - 1, result);
                                if (i == 0) {
                                    forecastSalesExplainList.set(j + i, "基于" + shortSupplyDays + "天断货累积递减");
                                } else {
                                    forecastSalesExplainList.set(j + i, "基于预测销量递增第" + i + "天");
                                }
                            }
                        }
                        shortSupplyIndex = -1;
                        shortSupplyDays = 0;
                    }
                }
                forecastInventory = inboundDecimal.add(forecastInventoryList.get(j - 1)).subtract(forecastSalesList.get(j - 1));
                //为负的情况下, 应该为0
                if (forecastInventory.compareTo(BigDecimal.ZERO) < 0) {
                    forecastInventory = BigDecimal.ZERO;
                }
                //库存先补断货
                addShortSupply = forecastInventory.subtract(forecastShortSupplyList.get(j - 1));
                if (addShortSupply.compareTo(BigDecimal.ZERO) < 0) {
                    forecastInventory = BigDecimal.ZERO;
                    forecastShortSupplyList.set(j, addShortSupply.abs());
                } else {
                    forecastShortSupplyList.set(j, BigDecimal.ZERO);
                    forecastInventory = addShortSupply;
                }
                BigDecimal subtract = forecastSalesList.get(j).subtract(forecastInventory);//今日断货
                shortSupply = subtract.compareTo(BigDecimal.ZERO) > 0 ? subtract : BigDecimal.ZERO;
                forecastShortSupplyList.set(j, forecastShortSupplyList.get(j).add(shortSupply));
            }

            //--------- 预测冗余, 预测断货 ---end
            forecastInventoryList.set(j, forecastInventory.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : forecastInventory.setScale(3, RoundingMode.HALF_EVEN));
            //在两倍周转期内, 如果预测销量 <= 库存, 那么又是可用的一天
            if (j < Constant.TURNOVER_MULTIPLE * turnoverDays && forecastSalesList.get(j).compareTo(forecastInventory) <= 0) {
                inventoryAvailableDays++;
            }

            //库存 - 销量, 继续跑, 这是周转期最后一天和周转期外的
            if (j >= turnoverDays) {
                if (j == turnoverDays) {            //周转期内总销量
                    if ((warehouseCode.equals(Constant.WAREHOUSE_JIT) && (turnoverSku.getBuyerName().equals(Constant.BUYER_JIT_1) || turnoverSku.getBuyerName().equals(Constant.BUYER_JIT_2))) || (warehouseCode.equals(Constant.WAREHOUSE_JIT1) && (turnoverSku.getBuyerName().equals(Constant.BUTER_JIT_5)))) {
//                        totalSales = dcAutoTurnover.getDailyWeightedSales().multiply(BigDecimal.valueOf(turnoverDays));
                        totalSales = dcAutoTurnover.getDailyWeightedSales().multiply(BigDecimal.valueOf(turnoverDays));
                        dcAutoTurnover.setTurnoverSales(totalSales);
                    } else {
                        dcAutoTurnover.setTurnoverSales(totalSales);
                    }
                }

                /*@deprecated  采购建议 = 安全库存缺少值  + 今日的实际缺货  + 本次采购到货日期前一天的累计销量 - 本次采购到货日期累计到货数量 + 预计销量 - 今日的实际库存  */
                /*采购建议 = 安全库存  + 今日的实际缺货  + 本次采购到货日期前一天的累计销量 - 本次采购到货日期累计到货数量 + 预计销量 - 今日的实际库存 */
                BigDecimal totalAdvised = totalSafeInventory
                        //20200818

                        /*@deprecated 安全库存缺少值= 理论安全库存  - 预测库存*/
//                        .subtract(forecastInventoryList.get(j > 0 ? j - 1 : 0))
                        .add(BigDecimal.valueOf(outStock.longValue()))//今天的实际缺货
                        .add(totalSales)//累计销量
                        .subtract(BigDecimal.valueOf(totalInbound.doubleValue()))//累计到货
                        .subtract(outStock > 0 ? BigDecimal.ZERO : BigDecimal.valueOf(realInventory.longValue())) //真实库存, 如果缺货就不减,不缺就减去
                        .add(forecastSalesList.get(j));//预计销量

                //采购建议数如果大于1的时候就向上取整。如果小于1就四舍五入
                if (totalAdvised.compareTo(BigDecimal.ONE) > 0) {
                    totalAdvised = totalAdvised.setScale(0, RoundingMode.CEILING);
                } else {
                    totalAdvised = totalAdvised.setScale(0, RoundingMode.HALF_EVEN);
                }
                forecastPurchaseAdvisedList.add(totalAdvised);
                if (totalAdvised.compareTo(moqDecimal) > 0) {
                    moqPurchaseAdvisedList.add(totalAdvised);
                } else if (totalAdvised.compareTo(BigDecimal.ZERO) <= 0) {
                    moqPurchaseAdvisedList.add(BigDecimal.ZERO);
                } else {
                    moqPurchaseAdvisedList.add(moqDecimal);
                }
            }

            totalSales = totalSales.add(forecastSalesList.get(j));


        }
        return inventoryAvailableDays;
    }

    /**
     * <p>
     * jb
     * </p>
     *
     * @param type                     1实际缺货2预计缺货3实际冗余4预计冗余5断
     * @param forecastFluctuationList  自动周转预测断货和冗余波动结果容器
     * @param recordTime               记录时间
     * @param forecastFluctuationDays  jb
     * @param forecastFluctuationIndex jb
     */
    private void handleForecastFluctuation(Integer type,
                                           List<DcAutoForecastFluctuation> forecastFluctuationList,
                                           LocalDate recordTime,
                                           int forecastFluctuationDays,
                                           int forecastFluctuationIndex) {

        DcAutoForecastFluctuation forecastFluctuation = new DcAutoForecastFluctuation();
        forecastFluctuation.setStartTime(recordTime.plusDays(forecastFluctuationIndex));
        forecastFluctuation.setEndTime(forecastFluctuation.getStartTime().plusDays(Math.abs(forecastFluctuationDays)));
        forecastFluctuation.setType(type);
        forecastFluctuationList.add(forecastFluctuation);
    }

    private Integer commonAdvise(DcBaseSku turnoverSku,
                                 String bailunSku,
                                 String warehouseCode,
                                 DcAutoTurnoverWithBLOBs dcAutoTurnover,
                                 Integer turnoverDays,
                                 Integer autoForecastDay,
                                 List<BigDecimal> forecastInventoryList,
                                 List<Integer> forecastInboundList,
                                 List<BigDecimal> forecastSalesList,
                                 List<BigDecimal> forecastShortSupplyList,
                                 List<BigDecimal> forecastPurchaseAdvisedList,
                                 List<BigDecimal> moqPurchaseAdvisedList,
                                 Integer realInventory,
                                 BigDecimal totalSafeInventory,
                                 Integer outStock,
                                 Integer inventoryAvailableDays,
                                 BigDecimal totalSales,
                                 BigDecimal totalInbound,
                                 BigDecimal moqDecimal,
                                 Map<String, BigDecimal> saveDaysMap) {
        BigDecimal inboundDecimal;
        BigDecimal forecastInventory;
        BigDecimal shortSupply;
        BigDecimal addShortSupply;
        for (int j = 0; j < autoForecastDay; j++) {
            inboundDecimal = BigDecimal.valueOf(forecastInboundList.get(j));
            totalInbound = totalInbound.add(inboundDecimal);
            if (j == turnoverDays) {
                dcAutoTurnover.setTurnoverInbound(totalInbound);
            }
            //计算预计库存 和 预计缺货
            if (j == 0) {
                if (outStock > 0) {
                    //如果有缺货数, 第一天预计库存 =  预计入库 - 缺货数
                    forecastInventory = BigDecimal.valueOf((forecastInboundList.get(j).doubleValue() - outStock.doubleValue()));
                    shortSupply = forecastInventory.compareTo(BigDecimal.ZERO) < 0 ? forecastInventory.abs() : BigDecimal.ZERO;//库存补入后的 缺货数
                    BigDecimal subtract = forecastSalesList.get(j).subtract(forecastInventory.compareTo(BigDecimal.ZERO) > 0 ? forecastInventory : BigDecimal.ZERO);//今天的缺货数
                    shortSupply = subtract.add(shortSupply).compareTo(BigDecimal.ZERO) > 0 ? subtract.add(shortSupply) : BigDecimal.ZERO;
                } else {
                    //否则, 第一天预计库存 =  真实库存 + 预计入库
                    forecastInventory = BigDecimal.valueOf((forecastInboundList.get(j).doubleValue() + realInventory.doubleValue()));
                    BigDecimal subtract = forecastSalesList.get(j).subtract(forecastInventory);
                    shortSupply = subtract.compareTo(BigDecimal.ZERO) > 0 ? subtract : BigDecimal.ZERO;
                }
                forecastShortSupplyList.set(j, forecastShortSupplyList.get(j).add(shortSupply));
            } else {
                forecastInventory = inboundDecimal.add(forecastInventoryList.get(j - 1)).subtract(forecastSalesList.get(j - 1));
                //库存先补断货
                //为负的情况下, 应该为0
                if (forecastInventory.compareTo(BigDecimal.ZERO) < 0) {
                    forecastInventory = BigDecimal.ZERO;
                }
                addShortSupply = forecastInventory.subtract(forecastShortSupplyList.get(j - 1));
                if (addShortSupply.compareTo(BigDecimal.ZERO) < 0) {
                    forecastInventory = BigDecimal.ZERO;
                    forecastShortSupplyList.set(j, addShortSupply.abs());
                } else {
                    forecastShortSupplyList.set(j, BigDecimal.ZERO);
                    forecastInventory = addShortSupply;
                }
                BigDecimal subtract = forecastSalesList.get(j).subtract(forecastInventory);//今日断货
                shortSupply = subtract.compareTo(BigDecimal.ZERO) > 0 ? subtract : BigDecimal.ZERO;
                forecastShortSupplyList.set(j, forecastShortSupplyList.get(j).add(shortSupply));
            }
            forecastInventoryList.set(j, forecastInventory.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : forecastInventory);
            //在两倍周转期内, 如果预测销量 <= 库存, 那么又是可用的一天
            if (j < Constant.TURNOVER_MULTIPLE * turnoverDays && forecastSalesList.get(j).compareTo(forecastInventory) <= 0) {
                inventoryAvailableDays++;
            }

            //库存 - 销量, 继续跑
            if (j >= turnoverDays) {
                BigDecimal totalAdvised = BigDecimal.ZERO;
                boolean firstAdvice = true;
                if (j == turnoverDays) {
                    if (warehouseCode.equals(Constant.WAREHOUSE_JIT) && (turnoverSku.getBuyerName().equals(Constant.BUYER_JIT_1) || turnoverSku.getBuyerName().equals(Constant.BUYER_JIT_2))) {
                        BigDecimal saveDays = saveDaysMap.get("saveDays");
                        totalSales = dcAutoTurnover.getDailyWeightedSales().multiply(BigDecimal.valueOf(turnoverDays));
                        dcAutoTurnover.setTurnoverSales(totalSales);
                        BigDecimal transNumber = BigDecimal.ZERO;
                        BigDecimal forecastInbound;
                        if (saveDays != null) {
                            for (int trans = 0; trans < autoForecastDay; trans++) {
                                forecastInbound = BigDecimal.valueOf(forecastInboundList.get(trans));
                                transNumber = transNumber.add(forecastInbound);
                            }
                            totalSales = dcAutoTurnover.getDailyWeightedSales().multiply(saveDays);
                            totalAdvised = totalSales.subtract(BigDecimal.valueOf(transNumber.doubleValue())) //累计到货
                                    .subtract(outStock > 0 ? BigDecimal.ZERO : BigDecimal.valueOf(realInventory.longValue())) //真实库存,如果缺货就不减
                                    .add(BigDecimal.valueOf(outStock.longValue())); //今天的实际缺货
                            firstAdvice = false;
                        }
                    } else if (warehouseCode.equals(Constant.WAREHOUSE_JIT1) && (turnoverSku.getBuyerName().equals(Constant.BUTER_JIT_5))) {
                        DcBaseStockMapper dcBaseStockMapper = SessionUtil.getSession().getMapper(DcBaseStockMapper.class);
                        DcBaseStock dcBaseStock = dcBaseStockMapper.selectOneByExample(DcBaseStockExample.newAndCreateCriteria().andBailunSkuEqualTo(bailunSku).andWarehouseCodeEqualTo(Constant.WAREHOUSE_JIT).example());

                        Integer purchase = 0;//采购在途
                        Integer transfer = 0;//调拨在途
                        Integer outStock01 = 0;//缺货数
                        DcMidTransit dcMidTransit;
                        try {
                            DcMidTransitMapper midTransitMapper = SessionUtil.getSession().getMapper(DcMidTransitMapper.class);
                            dcMidTransit = midTransitMapper.selectOneByExample(DcMidTransitExample.newAndCreateCriteria().andBailunSkuEqualTo(bailunSku).andWarehouseCodeEqualTo(Constant.WAREHOUSE_JIT).example());
                        } catch (Exception e) {
                            e.printStackTrace();
                            throw new RuntimeException("Mybatis操作DB失败", e);
                        } finally {
                            SessionUtil.closeSession();
                        }
                        if (dcMidTransit != null) {
                            purchase = dcMidTransit.getQuantityPurchase();
                            transfer = dcMidTransit.getQuantityTransfer();
                            outStock01 = dcMidTransit.getQuantityOutStock();
                        }
                        BigDecimal saveDays = saveDaysMap.get("saveDays");
                        totalSales = dcAutoTurnover.getDailyWeightedSales().multiply(BigDecimal.valueOf(turnoverDays));
                        dcAutoTurnover.setTurnoverSales(totalSales);
                        if (saveDays != null) {
                            totalSales = dcAutoTurnover.getDailyWeightedSales().multiply(saveDays);
                            totalAdvised = totalSales
                                    //真实库存,如果缺货就不减
                                    .subtract(outStock > 0 ? BigDecimal.ZERO : BigDecimal.valueOf(realInventory.longValue()))
                                    .subtract(outStock01 > 0 ? BigDecimal.ZERO : ((dcBaseStock != null && dcBaseStock.getUsableStock() != null) ? BigDecimal.valueOf(dcBaseStock.getUsableStock()) : BigDecimal.ZERO)) //广州014仓库存
                                    .subtract(BigDecimal.valueOf(purchase + transfer)); //广州001仓在途数
                            firstAdvice = false;
                        }
                    } else {
                        dcAutoTurnover.setTurnoverSales(totalSales);
                    }
                }
                if (firstAdvice) {
                    //采购建议 = 安全库存  + 今日的实际缺货  + 本次采购到货日期前一天的累计销量 - 本次采购到货日期累计到货数量 + 预计销量
                    /*@deprecated 采购建议 = 安全库存缺少值  + 今日的实际缺货  + 本次采购到货日期前一天的累计销量 - 本次采购到货日期累计到货数量 + 预计销量*/
                    totalAdvised = totalSafeInventory
//                            .subtract(forecastInventoryList.get(j > 0 ? j - 1 : 0))//安全库存缺少值= 理论安全库存  - 预测库存
                            .add(BigDecimal.valueOf(outStock.longValue()))//今天的实际缺货
                            .add(totalSales)//累计销量
                            .subtract(BigDecimal.valueOf(totalInbound.doubleValue()))//累计到货
                            .subtract(outStock > 0 ? BigDecimal.ZERO : BigDecimal.valueOf(realInventory.longValue())) //真实库存,如果缺货就不减
                            .add(forecastSalesList.get(Math.max(j - 1, 0)));//预计销量
                }

                //采购建议数如果大于1的时候就向上取整。如果小于1就四舍五入
                if (totalAdvised.compareTo(BigDecimal.ONE) > 0) {
                    totalAdvised = totalAdvised.setScale(0, RoundingMode.CEILING);
                } else {
                    totalAdvised = totalAdvised.setScale(0, RoundingMode.HALF_EVEN);
                }
                forecastPurchaseAdvisedList.add(totalAdvised);
                if (totalAdvised.compareTo(moqDecimal) > 0) {
                    moqPurchaseAdvisedList.add(totalAdvised);
                } else if (totalAdvised.compareTo(BigDecimal.ZERO) <= 0) {
                    moqPurchaseAdvisedList.add(BigDecimal.ZERO);
                } else {
                    moqPurchaseAdvisedList.add(moqDecimal);
                }
            }
            totalSales = totalSales.add(forecastSalesList.get(j));
        }
        return inventoryAvailableDays;
    }


    /**
     * 获取调拨头程 + 调拨打包 + 海外仓入库天数 的配置
     */
    private DcAutoConfigDelivery getDcAutoConfigDelivery(String bailunSku, String warehouseCode, DcBaseWarehouse dcBaseWarehouse) {
        DcAutoConfigDelivery dcAutoConfigDelivery = null;
        //如果不是国内仓, 就读配置表
        if (dcBaseWarehouse != null && !dcBaseWarehouse.getHqType().equals(CommonConstant.DOMESTIC_WAREHOUSE)) {
            try {
                DcAutoConfigDeliveryMapper dcAutoConfigDeliveryMapper = SessionUtil.getSession().getMapper(DcAutoConfigDeliveryMapper.class);
                dcAutoConfigDelivery = dcAutoConfigDeliveryMapper.selectOneByExample(DcAutoConfigDeliveryExample.newAndCreateCriteria().andVariableCodeEqualTo(warehouseCode).andBailunSkuEqualTo(bailunSku).andStatusEqualTo(1).andTypeEqualTo(1).example());
                if (dcAutoConfigDelivery == null) {
                    dcAutoConfigDelivery = dcAutoConfigDeliveryMapper.selectOneByExample(DcAutoConfigDeliveryExample.newAndCreateCriteria().andVariableCodeEqualTo(dcBaseWarehouse.getHqType()).andBailunSkuEqualTo(bailunSku).andStatusEqualTo(1).andTypeEqualTo(2).example());
                }
                if (dcAutoConfigDelivery == null) {
                    dcAutoConfigDelivery = dcAutoConfigDeliveryMapper.selectOneByExample(DcAutoConfigDeliveryExample.newAndCreateCriteria().andVariableCodeEqualTo(warehouseCode).andStatusEqualTo(1).andTypeEqualTo(3).example());
                }
                if (dcAutoConfigDelivery == null) {
                    dcAutoConfigDelivery = dcAutoConfigDeliveryMapper.selectOneByExample(DcAutoConfigDeliveryExample.newAndCreateCriteria().andVariableCodeEqualTo(dcBaseWarehouse.getHqType()).andStatusEqualTo(1).andTypeEqualTo(4).example());
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("Mybatis操作DB失败", e);
            } finally {
                SessionUtil.closeSession();
            }
        }
        if (dcAutoConfigDelivery == null) {
            dcAutoConfigDelivery = new DcAutoConfigDelivery(0, 0, 0);
        }
        return dcAutoConfigDelivery;
    }


    /**
     * 更新销量数据
     */
    private void assignmentToForecastSales(String bailunSku, String
            warehouseCode, List<BigDecimal> forecastSalesList) {
        if (forecastSalesList != null && forecastSalesList.size() > 0) {
            try {
                DcAutoSalesMapper autoSalesMapper1 = SessionUtil.getSession().getMapper(DcAutoSalesMapper.class);
                DcAutoSales dcAutoSales = autoSalesMapper1.selectOneByExample(DcAutoSalesExample.newAndCreateCriteria().andBailunSkuEqualTo(bailunSku).andWarehouseCodeEqualTo(warehouseCode).example());
                if (dcAutoSales != null) {
                    dcAutoSales.setDetails(forecastSalesList.toString());
                    dcAutoSales.setForecastTodaySales(forecastSalesList.get(0));
                    dcAutoSales.setForecastOnedaySales(forecastSalesList.get(1));
                    dcAutoSales.setForecastTwodaySales(forecastSalesList.get(2));
                    dcAutoSales.setForecastThreedaySales(forecastSalesList.get(3));
                    dcAutoSales.setForecastFourthdaySales(forecastSalesList.get(4));
                    dcAutoSales.setForecastFivedaySales(forecastSalesList.get(5));
                    dcAutoSales.setForecastSixdaySales(forecastSalesList.get(6));
                    dcAutoSales.setForecastSevendaySales(forecastSalesList.get(7));
                    dcAutoSales.setForecastEightdaySales(forecastSalesList.get(8));
                    dcAutoSales.setForecastNinedaySales(forecastSalesList.get(9));
                    dcAutoSales.setForecastTendaySales(forecastSalesList.get(10));
                    dcAutoSales.setForecastElevendaySales(forecastSalesList.get(11));
                    dcAutoSales.setForecastTwelvedaySales(forecastSalesList.get(12));
                    dcAutoSales.setForecastThridteendaySales(forecastSalesList.get(13));
                    dcAutoSales.setForecastFourteendaySales(forecastSalesList.get(14));
                    dcAutoSales.setForecastFifteendaySales(forecastSalesList.get(15));
                    dcAutoSales.setForecastSixteendaySales(forecastSalesList.get(16));
                    dcAutoSales.setForecastSeventeendaySales(forecastSalesList.get(17));
                    dcAutoSales.setForecastEighteendaySales(forecastSalesList.get(18));
                    dcAutoSales.setForecastNineteendaySales(forecastSalesList.get(19));
                    dcAutoSales.setForecastTwentydaySales(forecastSalesList.get(20));
                    dcAutoSales.setForecastTwentyOnedaySales(forecastSalesList.get(21));
                    dcAutoSales.setForecastTwentyTwodaySales(forecastSalesList.get(22));
                    dcAutoSales.setForecastTwentyThreedaySales(forecastSalesList.get(23));
                    dcAutoSales.setForecastTwentyFourthdaySales(forecastSalesList.get(24));
                    dcAutoSales.setForecastTwentyFivedaySales(forecastSalesList.get(25));
                    dcAutoSales.setForecastTwentySixdaySales(forecastSalesList.get(26));
                    dcAutoSales.setForecastTwentySevenedaySales(forecastSalesList.get(27));
                    dcAutoSales.setForecastTwentyEightdaySales(forecastSalesList.get(28));
                    dcAutoSales.setForecastTwentyNinedaySales(forecastSalesList.get(29));
                    dcAutoSales.setForecastThirtydaySales(forecastSalesList.get(30));
                    autoSalesMapper1.updateByPrimaryKeySelective(dcAutoSales);
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("Mybatis操作DB失败", e);
            } finally {
                SessionUtil.closeSession();
            }
        }
    }


    private void assignmentToForecastShortSupply(String bailunSku, String
            warehouseCode, List<BigDecimal> forecastShortSupplyList) {
        if (forecastShortSupplyList != null && forecastShortSupplyList.size() > 0) {
            DcAutoShortSupply dcAutoShortSupply = new DcAutoShortSupply();
            dcAutoShortSupply.setBailunSku(bailunSku);
            dcAutoShortSupply.setWarehouseCode(warehouseCode);
            dcAutoShortSupply.setForecastTodayShortSupply(forecastShortSupplyList.get(0));
            dcAutoShortSupply.setForecastOnedayShortSupply(forecastShortSupplyList.get(1));
            dcAutoShortSupply.setForecastTwodayShortSupply(forecastShortSupplyList.get(2));
            dcAutoShortSupply.setForecastThreedayShortSupply(forecastShortSupplyList.get(3));
            dcAutoShortSupply.setForecastFourthdayShortSupply(forecastShortSupplyList.get(4));
            dcAutoShortSupply.setForecastFivedayShortSupply(forecastShortSupplyList.get(5));
            dcAutoShortSupply.setForecastSixdayShortSupply(forecastShortSupplyList.get(6));
            dcAutoShortSupply.setForecastSevendayShortSupply(forecastShortSupplyList.get(7));
            dcAutoShortSupply.setForecastEightdayShortSupply(forecastShortSupplyList.get(8));
            dcAutoShortSupply.setForecastNinedayShortSupply(forecastShortSupplyList.get(9));
            dcAutoShortSupply.setForecastTendayShortSupply(forecastShortSupplyList.get(10));
            dcAutoShortSupply.setForecastElevendayShortSupply(forecastShortSupplyList.get(11));
            dcAutoShortSupply.setForecastTwelvedayShortSupply(forecastShortSupplyList.get(12));
            dcAutoShortSupply.setForecastThridteendayShortSupply(forecastShortSupplyList.get(13));
            dcAutoShortSupply.setForecastFourteendayShortSupply(forecastShortSupplyList.get(14));
            dcAutoShortSupply.setForecastFifteendayShortSupply(forecastShortSupplyList.get(15));
            dcAutoShortSupply.setForecastSixteendayShortSupply(forecastShortSupplyList.get(16));
            dcAutoShortSupply.setForecastSeventeendayShortSupply(forecastShortSupplyList.get(17));
            dcAutoShortSupply.setForecastEighteendayShortSupply(forecastShortSupplyList.get(18));
            dcAutoShortSupply.setForecastNineteendayShortSupply(forecastShortSupplyList.get(19));
            dcAutoShortSupply.setForecastTwentydayShortSupply(forecastShortSupplyList.get(20));
            dcAutoShortSupply.setForecastTwentyOnedayShortSupply(forecastShortSupplyList.get(21));
            dcAutoShortSupply.setForecastTwentyTwodayShortSupply(forecastShortSupplyList.get(22));
            dcAutoShortSupply.setForecastTwentyThreedayShortSupply(forecastShortSupplyList.get(23));
            dcAutoShortSupply.setForecastTwentyFourthdayShortSupply(forecastShortSupplyList.get(24));
            dcAutoShortSupply.setForecastTwentyFivedayShortSupply(forecastShortSupplyList.get(25));
            dcAutoShortSupply.setForecastTwentySixdayShortSupply(forecastShortSupplyList.get(26));
            dcAutoShortSupply.setForecastTwentySevenedayShortSupply(forecastShortSupplyList.get(27));
            dcAutoShortSupply.setForecastTwentyEightdayShortSupply(forecastShortSupplyList.get(28));
            dcAutoShortSupply.setForecastTwentyNinedayShortSupply(forecastShortSupplyList.get(29));
            dcAutoShortSupply.setForecastThirtydayShortSupply(forecastShortSupplyList.get(30));
            dcAutoShortSupply.setDetails(forecastShortSupplyList.toString());
            try {
                DcAutoShortSupplyMapper autoShortSupplyMapper = SessionUtil.getSession().getMapper(DcAutoShortSupplyMapper.class);
                int i = autoShortSupplyMapper.updateByExampleSelective(dcAutoShortSupply, DcAutoShortSupplyExample.newAndCreateCriteria().andBailunSkuEqualTo(dcAutoShortSupply.getBailunSku()).andWarehouseCodeEqualTo(dcAutoShortSupply.getWarehouseCode()).example());
                if (i == 0) {
                    autoShortSupplyMapper.insertSelective(dcAutoShortSupply);
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("Mybatis操作DB失败", e);
            } finally {
                SessionUtil.closeSession();
            }
        }
    }


    private void assignmentToForecastInbound(String bailunSku, String
            warehouseCode, Integer realInventory, List<Integer> forecastInboundList, List<String> forecastInboundRelationList) {
        DcAutoInbound dcAutoInbound = new DcAutoInbound();
        dcAutoInbound.setDetails(forecastInboundList.toString());
        dcAutoInbound.setRelation(forecastInboundRelationList.toString());
        dcAutoInbound.setBailunSku(bailunSku);
        dcAutoInbound.setWarehouseCode(warehouseCode);
        dcAutoInbound.setActualInventory(realInventory);
        dcAutoInbound.setForecastTodayInbound(forecastInboundList.get(0));
        dcAutoInbound.setForecastOnedayInbound(forecastInboundList.get(1));
        dcAutoInbound.setForecastTwodayInbound(forecastInboundList.get(2));
        dcAutoInbound.setForecastThreedayInbound(forecastInboundList.get(3));
        dcAutoInbound.setForecastFourthdayInbound(forecastInboundList.get(4));
        dcAutoInbound.setForecastFivedayInbound(forecastInboundList.get(5));
        dcAutoInbound.setForecastSixdayInbound(forecastInboundList.get(6));
        dcAutoInbound.setForecastSevendayInbound(forecastInboundList.get(7));
        dcAutoInbound.setForecastEightdayInbound(forecastInboundList.get(8));
        dcAutoInbound.setForecastNinedayInbound(forecastInboundList.get(9));
        dcAutoInbound.setForecastTendayInbound(forecastInboundList.get(10));
        dcAutoInbound.setForecastElevendayInbound(forecastInboundList.get(11));
        dcAutoInbound.setForecastTwelvedayInbound(forecastInboundList.get(12));
        dcAutoInbound.setForecastThridteendayInbound(forecastInboundList.get(13));
        dcAutoInbound.setForecastFourteendayInbound(forecastInboundList.get(14));
        dcAutoInbound.setForecastFifteendayInbound(forecastInboundList.get(15));
        dcAutoInbound.setForecastSixteendayInbound(forecastInboundList.get(16));
        dcAutoInbound.setForecastSeventeendayInbound(forecastInboundList.get(17));
        dcAutoInbound.setForecastEighteendayInbound(forecastInboundList.get(18));
        dcAutoInbound.setForecastNineteendayInbound(forecastInboundList.get(19));
        dcAutoInbound.setForecastTwentydayInbound(forecastInboundList.get(20));
        dcAutoInbound.setForecastTwentyOnedayInbound(forecastInboundList.get(21));
        dcAutoInbound.setForecastTwentyTwodayInbound(forecastInboundList.get(22));
        dcAutoInbound.setForecastTwentyThreedayInbound(forecastInboundList.get(23));
        dcAutoInbound.setForecastTwentyFourthdayInbound(forecastInboundList.get(24));
        dcAutoInbound.setForecastTwentyFivedayInbound(forecastInboundList.get(25));
        dcAutoInbound.setForecastTwentySixdayInbound(forecastInboundList.get(26));
        dcAutoInbound.setForecastTwentySevenedayInbound(forecastInboundList.get(27));
        dcAutoInbound.setForecastTwentyEightdayInbound(forecastInboundList.get(28));
        dcAutoInbound.setForecastTwentyNinedayInbound(forecastInboundList.get(29));
        dcAutoInbound.setForecastThirtydayInbound(forecastInboundList.get(30));
        try {
            DcAutoInboundMapper autoInboundMapper = SessionUtil.getSession().getMapper(DcAutoInboundMapper.class);
            int i = autoInboundMapper.updateByExampleSelective(dcAutoInbound, DcAutoInboundExample.newAndCreateCriteria().andBailunSkuEqualTo(dcAutoInbound.getBailunSku()).andWarehouseCodeEqualTo(dcAutoInbound.getWarehouseCode()).example());
            if (i == 0) {
                autoInboundMapper.insertSelective(dcAutoInbound);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Mybatis操作DB失败", e);
        } finally {
            SessionUtil.closeSession();
        }
    }


    private void assignmentToForecastInventory(String bailunSku, String warehouseCode, Integer
            realInventory, List<BigDecimal> forecastInventoryList) {
        if (forecastInventoryList != null && forecastInventoryList.size() > 0) {
            DcAutoInventory dcAutoInventory = new DcAutoInventory();
            dcAutoInventory.setBailunSku(bailunSku);
            dcAutoInventory.setWarehouseCode(warehouseCode);
            dcAutoInventory.setActualInventory(BigDecimal.valueOf(realInventory));
            dcAutoInventory.setForecastTodayInventory(forecastInventoryList.get(0));
            dcAutoInventory.setForecastOnedayInventory(forecastInventoryList.get(1));
            dcAutoInventory.setForecastTwodayInventory(forecastInventoryList.get(2));
            dcAutoInventory.setForecastThreedayInventory(forecastInventoryList.get(3));
            dcAutoInventory.setForecastFourthdayInventory(forecastInventoryList.get(4));
            dcAutoInventory.setForecastFivedayInventory(forecastInventoryList.get(5));
            dcAutoInventory.setForecastSixdayInventory(forecastInventoryList.get(6));
            dcAutoInventory.setForecastSevendayInventory(forecastInventoryList.get(7));
            dcAutoInventory.setForecastEightdayInventory(forecastInventoryList.get(8));
            dcAutoInventory.setForecastNinedayInventory(forecastInventoryList.get(9));
            dcAutoInventory.setForecastTendayInventory(forecastInventoryList.get(10));
            dcAutoInventory.setForecastElevendayInventory(forecastInventoryList.get(11));
            dcAutoInventory.setForecastTwelvedayInventory(forecastInventoryList.get(12));
            dcAutoInventory.setForecastThridteendayInventory(forecastInventoryList.get(13));
            dcAutoInventory.setForecastFourteendayInventory(forecastInventoryList.get(14));
            dcAutoInventory.setForecastFifteendayInventory(forecastInventoryList.get(15));
            dcAutoInventory.setForecastSixteendayInventory(forecastInventoryList.get(16));
            dcAutoInventory.setForecastSeventeendayInventory(forecastInventoryList.get(17));
            dcAutoInventory.setForecastEighteendayInventory(forecastInventoryList.get(18));
            dcAutoInventory.setForecastNineteendayInventory(forecastInventoryList.get(19));
            dcAutoInventory.setForecastTwentydayInventory(forecastInventoryList.get(20));
            dcAutoInventory.setForecastTwentyOnedayInventory(forecastInventoryList.get(21));
            dcAutoInventory.setForecastTwentyTwodayInventory(forecastInventoryList.get(22));
            dcAutoInventory.setForecastTwentyThreedayInventory(forecastInventoryList.get(23));
            dcAutoInventory.setForecastTwentyFourthdayInventory(forecastInventoryList.get(24));
            dcAutoInventory.setForecastTwentyFivedayInventory(forecastInventoryList.get(25));
            dcAutoInventory.setForecastTwentySixdayInventory(forecastInventoryList.get(26));
            dcAutoInventory.setForecastTwentySevenedayInventory(forecastInventoryList.get(27));
            dcAutoInventory.setForecastTwentyEightdayInventory(forecastInventoryList.get(28));
            dcAutoInventory.setForecastTwentyNinedayInventory(forecastInventoryList.get(29));
            dcAutoInventory.setForecastThirtydayInventory(forecastInventoryList.get(30));
            dcAutoInventory.setDetails(forecastInventoryList.toString());
            try {
                DcAutoInventoryMapper dcAutoInventoryMapper = SessionUtil.getSession().getMapper(DcAutoInventoryMapper.class);
                int i = dcAutoInventoryMapper.updateByExampleSelective(dcAutoInventory, DcAutoInventoryExample.newAndCreateCriteria().andBailunSkuEqualTo(dcAutoInventory.getBailunSku()).andWarehouseCodeEqualTo(dcAutoInventory.getWarehouseCode()).example());
                if (i == 0) {
                    dcAutoInventoryMapper.insertSelective(dcAutoInventory);
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("Mybatis操作DB失败", e);

            } finally {
                SessionUtil.closeSession();
            }
        }
    }


    public int getTotalPage(long count, Integer pageSize) {
        long totalPage;
        synchronized (AutoTurnoverJob.class) {
            if (count % pageSize.longValue() == 0) {
                totalPage = count / pageSize.longValue();
            } else {
                totalPage = count / pageSize.longValue() + 1;
            }
        }
        return (int) totalPage;
    }

    public BigDecimal getSaveDays(DcAutoSales dcAutoSales, DcAutoTurnoverWithBLOBs dcAutoTurnover, String bailunSku, String buyerName, String autoForecastDay, DcAutoStockUpRange dcAutoStockUpRange) {
        List<Integer> historySalesList = JSON.parseObject(dcAutoSales.getHistorySalesDetails(), new TypeReference<List<Integer>>() {
        });
        /*
         * dcAutoConfigSkuWarehouse不存在或者其status=0都是监控状态
         * 库存为0 并且 最近14天日均销量为0
         * 已经提醒实际缺货的，预计缺货就不提醒了
         * 已经提醒实际冗余的，预计冗余就不提醒了
         * 停止监控的不提醒
         */
        Integer historyFourteenSales = 0;
        Integer historySevenSales = 0;
        Integer historyThirtySales = 0;
        for (int i = 0; i < 30; i++) {
            if (i < 7) {
                historySevenSales += historySalesList.get(23 + i);
            }
            if (i < 14) {
                historyFourteenSales += historySalesList.get(16 + i);
            }
            historyThirtySales += historySalesList.get(i);
        }
        BigDecimal avgHistorySevendaySales = BigDecimal.valueOf(historySevenSales.longValue()).divide(Constant.BIGDECIMAL_SEVEN, 3, RoundingMode.HALF_EVEN);
        BigDecimal avgHistoryFourteendaySales = BigDecimal.valueOf(historyFourteenSales.longValue()).divide(Constant.BIGDECIMAL_FOURTEEN, 3, RoundingMode.HALF_EVEN);
        BigDecimal avgHistoryThirtydaySales = BigDecimal.valueOf(historyThirtySales.longValue()).divide(Constant.BIGDECIMAL_THIRTY, 3, RoundingMode.HALF_EVEN);

        List<DcAutoJitTag> dcAutoJitTags;
        DcAutoJitTagMapper dcAutoJitTagMapper = SessionUtil.getSession().getMapper(DcAutoJitTagMapper.class);
        if (dcAutoStockUpRange != null) {
            dcAutoJitTags = dcAutoJitTagMapper.selectByExample(DcAutoJitTagExample.newAndCreateCriteria().andGroupIdEqualTo(dcAutoStockUpRange.getGroupId()).example());
        } else {
            dcAutoJitTags = dcAutoJitTagMapper.selectByExample(DcAutoJitTagExample.newAndCreateCriteria().andGroupIdEqualTo(1).example());
        }
        BigDecimal saveDays = BigDecimal.ZERO;

        if (dcAutoJitTags != null) {
            boolean hitTag = false;

            for (DcAutoJitTag dcAutoJitTag : dcAutoJitTags) {
                if (dcAutoJitTag.getEnable7() == 1) {
                    switch (dcAutoJitTag.getIsGt7()) {
                        case 1:
                            if (avgHistorySevendaySales.compareTo(dcAutoJitTag.getQuantitySales7()) > 0) {
                                break;
                            } else {
                                continue;
                            }
                        case 2:
                            if (avgHistorySevendaySales.compareTo(dcAutoJitTag.getQuantitySales7()) == 0) {
                                break;
                            } else {
                                continue;
                            }
                        case 3:
                            if (avgHistorySevendaySales.compareTo(dcAutoJitTag.getQuantitySales7()) < 0) {
                                break;
                            } else {
                                continue;
                            }
                        case 4:
                            if (avgHistorySevendaySales.compareTo(dcAutoJitTag.getQuantitySales7()) > -1) {
                                break;
                            } else {
                                continue;
                            }
                        case 5:
                            if (avgHistorySevendaySales.compareTo(dcAutoJitTag.getQuantitySales7()) < 1) {
                                break;
                            } else {
                                continue;
                            }
                        default:
                            continue;
                    }
                }
                if (dcAutoJitTag.getEnable14() == 1) {
                    switch (dcAutoJitTag.getIsGt14()) {
                        case 1:
                            if (avgHistoryFourteendaySales.compareTo(dcAutoJitTag.getQuantitySales14()) > 0) {
                                break;
                            } else {
                                continue;
                            }
                        case 2:
                            if (avgHistoryFourteendaySales.compareTo(dcAutoJitTag.getQuantitySales14()) == 0) {
                                break;
                            } else {
                                continue;
                            }
                        case 3:
                            if (avgHistoryFourteendaySales.compareTo(dcAutoJitTag.getQuantitySales14()) < 0) {
                                break;
                            } else {
                                continue;
                            }
                        case 4:
                            if (avgHistoryFourteendaySales.compareTo(dcAutoJitTag.getQuantitySales14()) > -1) {
                                break;
                            } else {
                                continue;
                            }
                        case 5:
                            if (avgHistoryFourteendaySales.compareTo(dcAutoJitTag.getQuantitySales14()) < 1) {
                                break;
                            } else {
                                continue;
                            }
                        default:
                            continue;
                    }
                }
                if (dcAutoJitTag.getEnable30() == 1) {
                    switch (dcAutoJitTag.getIsGt30()) {
                        case 1:
                            if (avgHistoryThirtydaySales.compareTo(dcAutoJitTag.getQuantitySales30()) > 0) {
                                break;
                            } else {
                                continue;
                            }
                        case 2:
                            if (avgHistoryThirtydaySales.compareTo(dcAutoJitTag.getQuantitySales30()) == 0) {
                                break;
                            } else {
                                continue;
                            }
                        case 3:
                            if (avgHistoryThirtydaySales.compareTo(dcAutoJitTag.getQuantitySales30()) < 0) {
                                break;
                            } else {
                                continue;
                            }
                        case 4:
                            if (avgHistoryThirtydaySales.compareTo(dcAutoJitTag.getQuantitySales30()) > -1) {
                                break;
                            } else {
                                continue;
                            }
                        case 5:
                            if (avgHistoryThirtydaySales.compareTo(dcAutoJitTag.getQuantitySales30()) < 1) {
                                break;
                            } else {
                                continue;
                            }
                        default:
                            continue;
                    }
                }

                if (StringUtils.isNotBlank(buyerName) &&
                        StringUtils.isNotBlank(dcAutoJitTag.getBuyerName())
                        && !buyerName.equals(dcAutoJitTag.getBuyerName())) {
                    continue;
                }
                saveDays = dcAutoJitTag.getQuantitySafeInventory();
                if ("weightingAvgSales".equals(autoForecastDay)) {
                    DcAutoJitTagLogMapper dcAutoJitTagLogMapper = SessionUtil.getSession().getMapper(DcAutoJitTagLogMapper.class);
                    DcAutoJitTagLog dcAutoJitTagLog = new DcAutoJitTagLog();
                    dcAutoJitTagLog.setBailunSku(bailunSku);
                    dcAutoJitTagLog.setDate(LocalDate.now());
                    dcAutoJitTagLog.setQuantitySales7(avgHistorySevendaySales);
                    dcAutoJitTagLog.setQuantitySales14(avgHistoryFourteendaySales);
                    dcAutoJitTagLog.setQuantitySales30(avgHistoryThirtydaySales);
                    dcAutoJitTagLog.setTagId(dcAutoJitTag.getId());
                    dcAutoJitTagLog.setTagJson(JSONObject.toJSONString(dcAutoJitTag));
                    dcAutoJitTagLogMapper.upsertSelective(dcAutoJitTagLog);
                    dcAutoTurnover.setTagId(dcAutoJitTag.getId());
                    hitTag = true;
                }
                break;
            }

            //如果一条规则都没命中
            if (!hitTag) {
                dcAutoTurnover.setTagId(0);
            }
        }
        return saveDays;
    }
}
