package com.prolog.eis.service.core.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.codingapi.tx.annotation.TxTransaction;
import com.prolog.eis.api.StrategyApi;
import com.prolog.eis.common.util.PrologStringUtils;
import com.prolog.eis.common.util.location.LocationConstants;
import com.prolog.eis.constants.base.*;
import com.prolog.eis.constants.cfg.LocationCfgConstants;
import com.prolog.eis.constants.cfg.StationCfgConstants;
import com.prolog.eis.constants.sys.SysParamConstants;
import com.prolog.eis.constants.wms.WmsConstants;
import com.prolog.eis.core.dto.route.AreaLocationCountDto;
import com.prolog.eis.core.dto.route.CarryTaskCallbackDto;
import com.prolog.eis.core.model.biz.carry.CarryTask;
import com.prolog.eis.core.model.biz.route.ContainerLocation;
import com.prolog.eis.dao.container.*;
import com.prolog.eis.dao.inbound.BizInboundTaskDetailMapper;
import com.prolog.eis.dao.inbound.BizInboundTaskMapper;
import com.prolog.eis.dao.outbound.BizOutboundTaskDetailHisMapper;
import com.prolog.eis.dao.outbound.BizOutboundTaskDetailMapper;
import com.prolog.eis.dao.outbound.BizOutboundTaskHisMapper;
import com.prolog.eis.dao.outbound.BizOutboundTaskMapper;
import com.prolog.eis.dao.statistics.EfficiencyStatisticsMapper;
import com.prolog.eis.dto.item.ItemSyncVO;
import com.prolog.eis.dto.outbound.ContainerTaskDismantleDto;
import com.prolog.eis.dto.outbound.WmsOutCbReqDto;
import com.prolog.eis.dto.outbound.WmsOutDetailCbReqDto;
import com.prolog.eis.dto.strategy.ContainerInfoDto;
import com.prolog.eis.dto.strategy.OrderDetailDto;
import com.prolog.eis.dto.strategy.OrderDto;
import com.prolog.eis.enums.base.BaseStatusEnum;
import com.prolog.eis.enums.base.BaseTypeEnum;
import com.prolog.eis.enums.station.StationTypeEnum;
import com.prolog.eis.enums.wms.WmsCarryTaskInCode;
import com.prolog.eis.model.container.*;
import com.prolog.eis.model.inbound.BizInboundTask;
import com.prolog.eis.model.inbound.BizInboundTaskDetail;
import com.prolog.eis.model.out.ContainerTaskDismantle;
import com.prolog.eis.model.outbound.BizOutboundTask;
import com.prolog.eis.model.outbound.BizOutboundTaskDetail;
import com.prolog.eis.model.outbound.BizOutboundTaskDetailHis;
import com.prolog.eis.model.outbound.BizOutboundTaskHis;
import com.prolog.eis.model.pick.PickingOrder;
import com.prolog.eis.model.shelves.PalletInfo;
import com.prolog.eis.model.statistics.EfficiencyStatistics;
import com.prolog.eis.outbound_dispatch.DispatchStrategy;
import com.prolog.eis.service.container.ContainerInfoService;
import com.prolog.eis.service.container.ContainerTaskService;
import com.prolog.eis.service.container.LocationCfgService;
import com.prolog.eis.service.container.StationCfgService;
import com.prolog.eis.service.core.OutboundService;
import com.prolog.eis.service.feign.FeignGoodsService;
import com.prolog.eis.service.feign.RouteFeignService;
import com.prolog.eis.service.pick.PickingOrderService;
import com.prolog.eis.service.pms.PmsService;
import com.prolog.eis.service.wms.WmsOutBackService;
import com.prolog.eis.store_init.StoreInitStrategy;
import com.prolog.framework.common.message.RestMessage;
import com.prolog.framework.core.exception.PrologException;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Order;
import com.prolog.framework.core.restriction.Restrictions;
import com.prolog.framework.utils.AssertUtils;
import com.prolog.framework.utils.MapUtils;
import com.prolog.framework.utils.ObjectUtils;
import com.prolog.framework.utils.StringUtils;
import com.prolog.fx.inf.core.Exception.RetryException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class OutboundServiceImpl implements OutboundService {

    //补货出库到拆零上架站台 标识，这里不做出库搬运，走上架搬运流程
    public static final String SUPPLY_FLAG_NO_CARRY = "SUPPLY_FLAG_NO_CARRY";

    private final ContainerInfoService containerInfoService;
    private final BizOutboundTaskMapper bizOutboundTaskMapper;
    private final BizOutboundTaskDetailMapper bizOutboundTaskDetailMapper;
    private final ContainerInfoDetailMapper containerInfoDetailMapper;
    private final ContainerInfoMapper containerInfoMapper;
    private final ContainerTaskMapper containerTaskMapper;
    private final WmsOutBackService wmsOutBackService;
    private final RouteFeignService plgFeignRouteService;
    private final LocationCfgService locationCfgService;
    private final BizOutboundTaskHisMapper bizOutboundTaskHisMapper;
    private final BizOutboundTaskDetailHisMapper bizOutboundTaskDetailHisMapper;
    private final ContainerTaskService containerTaskService;
    private final ContainerStoreChangeMapper containerStoreChangeMapper;
    private final PickingOrderService pickingOrderService;
    private final StationCfgService stationCfgService;
    private final PmsService pmsService;
    private final BizInboundTaskMapper bizInboundTaskMapper;
    private final BizInboundTaskDetailMapper bizInboundTaskDetailMapper;
    private final ContainerTaskDismantleMapper containerTaskDismantleMapper;
    private final PalletInfoMapper palletInfoMapper;
    private final FeignGoodsService feignGoodsService;
    private final RedisTemplate redisTemplate;
    private final EfficiencyStatisticsMapper efficiencyStatisticsMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void dispatch(String billType, String warehouseArea, DispatchStrategy dispatchStrategy, StoreInitStrategy storeInitStrategy) throws Exception {
        StrategyApi.doSwitchDispatch(dispatchStrategy, billType, warehouseArea, storeInitStrategy);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendPmsCarryTask() throws Exception {
        Criteria criteria = new Criteria(ContainerTask.class);
        criteria.setRestriction(Restrictions.eq("status", ContainerTaskStatusConstants.IN_PLACE_STATUS).and(Restrictions.in("taskType", new Integer[]{10, 20, 30})));
        criteria.setOrder(Order.newInstance().desc("finishTime"));
        //查询待出库订单
        List<ContainerTask> containers = containerTaskMapper.findByCriteria(criteria);
        if (CollectionUtils.isEmpty(containers)) {
            log.debug("暂无到出库口的出库托盘");
            return;
        }
        ContainerTask containerTask = containers.get(0);
        //处理机械手的缓存数量
        List<StationCfg> stationCfgList = stationCfgService.findByMap(MapUtils.put("isLock", 0).put("isAsk", 0).getMap());
        AssertUtils.isNotTrue(CollectionUtils.isEmpty(stationCfgList), "站台全部被锁定");
        StationCfg jxsStation = stationCfgList.stream().filter(o -> o.getAreaNo().equals(containerTask.getExtend2())).findFirst().orElse(null);
        AssertUtils.isNotTrue(jxsStation == null, String.format("托盘所去的当前机械手站台:[%s]被锁定或未配置", containerTask.getExtend2()));
      /*  StationCfg crrStation = stationCfgList.stream().filter(o -> o.getAreaNo().equals(containerTask.getExtend3())).findFirst().orElse(null);
        AssertUtils.isNotTrue(crrStation == null,String.format("托盘所在的当前出库口站台:[%s]被锁定或未配置",containerTask.getExtend3()));*/
        AssertUtils.isNotTrue(jxsStation.getMaxCachePosition() <= jxsStation.getCurrentCacheNum(), String.format("当前所去的机械手：[%s]缓存位已满，等待中", containerTask.getExtend2()));

        String containerNo = containerTask.getContainerNo();
        if (!StringUtils.isEmpty(jxsStation.getCurrentContainerNo())) {
            containerNo = jxsStation.getCurrentContainerNo() + "," + containerTask.getContainerNo();
        }
        //增加缓存数
        stationCfgService.updateMapById(jxsStation.getId(), MapUtils.put("currentCacheNum", jxsStation.getCurrentCacheNum() + 1).put("updateTime", new Date()).put("currentContainerNo", containerNo).getMap());

        //修改状态
        //修改状态为到位
        criteria = new Criteria(ContainerTask.class);
        criteria.setRestriction(Restrictions.eq("containerNo", containerTask.getContainerNo()));
        long s = containerTaskMapper.updateMapByCriteria(MapUtils.put("status", ContainerTaskStatusConstants.CARRY_RUNNING).put("updateTime", LocalDateTime.now()).getMap(), criteria);

        pmsService.carryTask(containerTask.getContainerNo(), containerTask.getExtend3(), jxsStation.getAreaNo());

        //清除容器点位
        plgFeignRouteService.containerOffShelf(containerNo);
    }

    @Override
    public void doEmptyOutboundCallBack(CarryTaskCallbackDto carryTaskCallbackDto) {
        final String containerNo = carryTaskCallbackDto.getContainerNo();
        containerInfoMapper.deleteByMap(MapUtils.put("containerNo", containerNo).getMap(), ContainerInfo.class);
        containerInfoDetailMapper.deleteByMap(MapUtils.put("containerNo", containerNo).getMap(), ContainerInfoDetail.class);
    }


    /**
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @TxTransaction(rollbackFor = Exception.class)
    public void doWmsOutTask(BizOutboundTask outTask, String containerNo, List<ContainerTask> containerTaskList) throws Exception {
        List<ContainerTask> tempTask = containerTaskList.stream().filter(o -> o.getContainerNo().equals(containerNo)).collect(Collectors.toList());
        ContainerTask containerTask = tempTask.get(0);
        //查询是否为在途追加的托盘
        Long count = additionalTaskLogic(containerTask.getContainerNo());
        ContainerInfo containerInfo = containerInfoService.findOneByContainerNo(containerTask.getContainerNo());
        if (count > 0 || containerInfo.getTaskType() != 0) {
            log.info(String.format("发送出库搬运任务：托盘：[%s]正在搬运，等待中", containerTask.getContainerNo()));
            return;
        }
        //分配出库口
        String targetAreaNo = getOutAreaNo(containerTask, outTask, tempTask);
        List<AreaLocationCountDto> sonLocationCountDtoList = plgFeignRouteService.queryAreaLocationCountDtoList(Collections.singletonList(targetAreaNo));

        if (CollectionUtils.isEmpty(sonLocationCountDtoList)) {
            throw new Exception(String.format("目标区域：[%s]没有查询到route数据", targetAreaNo));
        } else {
            List<AreaLocationCountDto> collect = sonLocationCountDtoList.stream().filter(o -> o.getFreeCount() > 0 && o.getAreaNo().equals(targetAreaNo)).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(collect)) {
                throw new Exception(String.format("目标区域：[%s]没有可用的库位", targetAreaNo));
            }
        }

        //判断如果当前的托盘为拼托 查有无追加的托盘为多订单整托的（针对追加成整托的托盘发送去整托站台（已发送的任务改变不了目标区域））
        updateContainerType(containerTask);
        Criteria criteria = new Criteria(ContainerInfo.class);
        criteria.setRestriction(Restrictions.eq("containerNo", containerTask.getContainerNo()));
        containerInfoService.updateByMap(MapUtils.put("taskType", BaseTaskTypeConstants.OUTBOUND_TYPE).getMap(), criteria);

        containerTaskService.updateStatusBatch(tempTask, ContainerTaskStatusConstants.START_STATUS);

        doUpdateOutTaskDetail(tempTask);

        if (!StrUtil.equals(SUPPLY_FLAG_NO_CARRY, targetAreaNo)) {
            CarryTask carryTask = new CarryTask();
            carryTask.setId(StrUtil.uuid());
            carryTask.setPalletNo(containerTask.getContainerNo());
            carryTask.setContainerNo(containerTask.getContainerNo());
            carryTask.setTaskType(LocationConstants.PATH_TASK_TYPE_CARRY);
            carryTask.setEndRegion(targetAreaNo);
            carryTask.setCreateTime(new Date());
            carryTask.setBusinessType(String.valueOf(BaseTaskTypeConstants.OUTBOUND_TYPE));
            carryTask.setTaskRemark("出库搬运");
            carryTask.setPriority(containerTask.getPriorty());
            plgFeignRouteService.createBatchCarry(Arrays.asList(carryTask));
        }
    }

    private void updateContainerType(ContainerTask containerTask) {
        if (containerTask.getContainerType() == ContainerTaskStatusConstants.HALF_TRAY_TYPE) {
            Long count = containerTaskMapper.findCountByMap(MapUtils.put("containerNo", containerTask.getContainerNo()).put("containerType", ContainerTaskStatusConstants.MORE_FULL_TRAY_TYPE).getMap(), ContainerTask.class);
            if (count > 0) {
                containerTask.setContainerType(ContainerTaskStatusConstants.MORE_FULL_TRAY_TYPE);
                containerTaskMapper.updateMapById(containerTask.getId(), MapUtils.put("containerType", containerTask.getContainerType()).getMap(), ContainerTask.class);
            }
        }
    }


    /**
     * 更新出库明细到位数量和状态.
     * 1.更新出库明细实际商品数
     * 2.更新容器任务状态
     *
     * @param collectList
     */
    private void doUpdateOutTaskDetail(List<ContainerTask> collectList) throws Exception {
        for (ContainerTask containerTask : collectList) {
            BizOutboundTaskDetail outTaskDetail = bizOutboundTaskDetailMapper.findById(containerTask.getOutboundDetailId(), BizOutboundTaskDetail.class);
            if (outTaskDetail == null) {
                throw new Exception("出库任务明细不存在，任务ID：" + containerTask.getOutboundDetailId());
            }
            //更新出库任务状态
            outTaskDetail.setActQty(new BigDecimal(outTaskDetail.getActQty() + "").add(new BigDecimal(containerTask.getQty() + "")));
            outTaskDetail.setUpdateTime(LocalDateTime.now());
            if (outTaskDetail.getActQty().compareTo(outTaskDetail.getPlanQty()) >= 0) {
                outTaskDetail.setStatus(BaseStatusEnum.STATUS_CARRY.getCode());
            }
            bizOutboundTaskDetailMapper.update(outTaskDetail);
        }
    }

    private Long additionalTaskLogic(String containerNo) {
        Criteria criteria = Criteria.forClass(ContainerTask.class);
        criteria.restrictionBuilder().gt(ContainerTask::getStatus, BaseStatusEnum.STATUS_INIT.getCode()).eq(ContainerTask::getContainerNo, containerNo).build();
        Long count = containerTaskService.findCountByCriteria(criteria);
        return count;
    }

    /**
     * 根据库区判断站台
     *
     * @param containerTask
     * @return
     * @throws Exception
     */
    public String getOutAreaNo(ContainerTask containerTask, BizOutboundTask outTask, List<ContainerTask> tempTask) throws Exception {
        List<StationCfg> stationAllList = stationCfgService.findByMap(MapUtils.put("isLock", 0).put("isAsk", 0).getMap());
        AssertUtils.isNotTrue(stationAllList.isEmpty(), "出库站台未配置或已锁定");
        List<StationCfg> stationList = new ArrayList<>();
        if (containerTask.getWmsLocationCode().equals(WmsConstants.MJZ)) {
            //四向库正常出库
            if (containerTask.getTaskType() == BaseBillTypeConstants.OUTBOUND_TYPE) {
                stationList = stationAllList.stream().filter(o -> o.getStationType() == StationTypeEnum.SXKZT_TYPE.getCode()).collect(Collectors.toList());
                AssertUtils.isNotTrue(CollectionUtils.isEmpty(stationList), "四向库整托出库站台未配置或已锁定");
                if (containerTask.getContainerType() == ContainerTaskStatusConstants.HALF_TRAY_TYPE) {
                    stationList = stationAllList.stream().filter(o -> o.getStationType() == StationTypeEnum.SXKJX_TYPE.getCode() && o.getTaskType() == StationCfgConstants.OUT).collect(Collectors.toList());
                    AssertUtils.isNotTrue(CollectionUtils.isEmpty(stationList), "四向库拣选站台未配置，或不是出库站台");
                }
            } else if (containerTask.getTaskType() == BaseBillTypeConstants.MAINTAIN_TYPE || containerTask.getTaskType() == BaseBillTypeConstants.INVENTORY_TYPE) {
                //四向库盘点、养护出库
                stationList = stationAllList.stream().filter(o -> o.getStationType() == StationTypeEnum.SXKJX_TYPE.getCode() && o.getTaskType() == StationCfgConstants.INVENTORY).collect(Collectors.toList());
                AssertUtils.isNotTrue(CollectionUtils.isEmpty(stationList), "四向库拣选站台未配置，或不是盘点站台");
            } else if (containerTask.getTaskType() == BaseBillTypeConstants.REPLENISH_TYPE) {
                //四向库补货出库（预留）1.整托情况下判断拆零区托盘库有无空闲货位   如果无空闲  校验拆零上架站台任务数   叫料料托
                return supplyTask(containerTask, outTask, tempTask);
            } else {
                throw new Exception("出库类型BillType未定义");
            }
        } else {
            //二楼库区
            //补货出库
            if (containerTask.getTaskType() == BaseBillTypeConstants.REPLENISH_TYPE) {
                //四向库补货出库（预留）
                return supplyTask(containerTask, outTask, tempTask);
            } else if (Arrays.asList(BaseBillTypeConstants.INVENTORY_TYPE, BaseBillTypeConstants.MAINTAIN_TYPE).contains(containerTask.getTaskType())) {
                stationList = stationAllList.stream().filter(o -> o.getStationType() == StationTypeEnum.JX_TYPE.getCode() && o.getTaskType() == 20).collect(Collectors.toList());
                AssertUtils.isNotTrue(CollectionUtils.isEmpty(stationAllList), "二楼盘点养护站台未配置或已锁定");
            } else {
                //判断单订单整托发往1楼
                if (containerTask.getContainerType() == ContainerTaskStatusConstants.ONE_FULL_TRAY_TYPE) {
                    stationList = stationAllList.stream().filter(o -> o.getStationType() == StationTypeEnum.DDDZT_TYPE.getCode()).collect(Collectors.toList());
                    AssertUtils.isNotTrue(CollectionUtils.isEmpty(stationAllList), "单订单整托站台未配置或已锁定");
                } else {
                    stationList = stationAllList.stream().filter(o -> o.getStationType() == StationTypeEnum.JX_TYPE.getCode() && o.getTaskType() == 10).collect(Collectors.toList());
                    AssertUtils.isNotTrue(CollectionUtils.isEmpty(stationAllList), "二楼拣选站台未配置或已锁定");
                }
            }
        }

        List<ContainerLocation> containerLocationVos = plgFeignRouteService.findContainerLocationByTargetAreaNos(stationList.stream().map(StationCfg::getAreaNo).distinct().collect(Collectors.toList()));
        Map<String, Long> taskNum = containerLocationVos.stream().collect(Collectors.groupingBy(ContainerLocation::getTargetArea, Collectors.counting()));
        //获取区域任务数
        for (StationCfg station : stationList) {
            station.setTaskNum(0l);
            if (taskNum.containsKey(station.getAreaNo())) {
                station.setTaskNum(taskNum.get(station.getAreaNo()));
            }
        }
        stationList = stationList.stream().sorted(Comparator.comparing(StationCfg::getTaskNum)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(stationList)) {
            throw new PrologException("没有可用站台");
        }
        return stationList.get(0).getAreaNo();
    }

    //补货
    @Override
    public String supplyTask(ContainerTask containerTask, BizOutboundTask outTask, List<ContainerTask> tempTask) throws Exception {
        //1 判断当前容器是否是整托
        long count2 = tempTask.stream().filter(item -> item.getTaskType() != BaseBillTypeConstants.REPLENISH_TYPE).count();
        long count1 = tempTask.stream().filter(item -> item.getContainerType() > 0).count();
        if (count1 > 0 && count2 == 0) {
            //2 整托情况下判断agv拆零托盘库是否有空闲货位
            //查询agv拆零托盘库剩余可用货位数
            LocationCfg locationCfg = locationCfgService.findOneByMap(MapUtils.put("type", LocationCfgConstants.AGVL_TP).put("wmsAreaNo", WmsCarryTaskInCode.AGVL.getCode()).getMap());
            AreaLocationCountDto areaLocationCountDto = plgFeignRouteService.queryAreaLocationCountDtoList(Arrays.asList(locationCfg.getAreaNo())).stream().findFirst().orElseThrow(() -> new PrologException("未查询到拆零托盘库剩余货位"));
            //查询站台托盘数量
            Criteria criteria = new Criteria(StationCfg.class);
            criteria.restrictionBuilder().in(StationCfg::getStationType, Arrays.asList(StationTypeEnum.CL_TYPE.getCode(), StationTypeEnum.ZJ_TYPE.getCode())).build();
            List<String> stationAreaList = stationCfgService.findByCriteria(criteria).stream().map(StationCfg::getAreaNo).collect(Collectors.toList());
            List<String> containerList = plgFeignRouteService.findCarryTaskListByEndRegionList(stationAreaList).stream().map(CarryTask::getContainerNo).collect(Collectors.toList());
            Criteria containerCr = new Criteria(ContainerInfoDetail.class);
            containerCr.restrictionBuilder().in(ContainerInfoDetail::getContainerNo, containerList).eq(ContainerInfoDetail::getContainerType, 3).build();
            long count = containerInfoDetailMapper.findCountByCriteria(containerCr);
            //判断托盘库是否有空闲货位
            if (areaLocationCountDto.getFreeCount() - count > 0) {
                ItemSyncVO itemSyncVO = feignGoodsService.queryItemMst(Arrays.asList(containerTask.getItemId())).stream().findFirst().get();
                if (StrUtil.equals(itemSyncVO.getAbcType(), AbcTypeConstant.TYPE_A)) {
                    //3 有空闲货位直接搬运到agv拆零托盘库
                    CarryTask carryTask = setCarryTask(containerTask, locationCfg.getAreaNo(), containerTask.getItemId() + "@@" + containerTask.getLotId(), String.valueOf(BaseTaskTypeConstants.AGVL_TYPE));
                    plgFeignRouteService.createBatchCarry(Arrays.asList(carryTask));
                    //修改库存库区
                    ContainerInfo containerInfo = containerInfoService.findByMap(MapUtils.put("containerNo", containerTask.getContainerNo()).getMap()).get(0);
                    containerInfo.setWarehouseArea(WmsCarryTaskInCode.AGVL.getCode());
                    containerInfo.setAreaType(AreaTypeConstant.AGV_CL);
                    containerInfo.setTaskType(ContainerTaskTypeConstants.CHANGE_INBOUND);
                    containerInfo.setUpdateTime(LocalDateTime.now());
                    containerInfoService.update(containerInfo);
                    Criteria containerDetailCri = new Criteria(ContainerInfoDetail.class);
                    containerDetailCri.restrictionBuilder().eq(ContainerInfoDetail::getContainerNo, containerTask.getContainerNo()).build();
                    containerInfoDetailMapper.updateMapByCriteria(MapUtils.put("areaType", AreaTypeConstant.AGV_CL).put("updateTime", LocalDateTime.now()).getMap(), containerDetailCri);
                    return SUPPLY_FLAG_NO_CARRY;
                }
            }
        }
        BigDecimal reduce = tempTask.stream().filter(item -> item.getTaskType() == BaseBillTypeConstants.REPLENISH_TYPE).map(ContainerTask::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);
        //4 非整托或者agv拆零托盘库没有空闲货位搬运到拆零上架站台 保存入库上架任务
        BizInboundTask bizInboundTask = new BizInboundTask();
        bizInboundTask.setEnterpriseId(outTask.getEnterpriseId());
        bizInboundTask.setBillNo(outTask.getBillNo());
        bizInboundTask.setWmsLocationCode(WmsCarryTaskInCode.AGVL.getCode());
        bizInboundTask.setContainerType(ContainerInfoTypeConstants.GOODS_PALLET);
        bizInboundTask.setBusinessType(BizInboundTask.SUPPLY_BUSINESS_TYPE);
        bizInboundTask.setWmsWarehouseId(outTask.getWmsWarehouseId());
        bizInboundTask.setWorkType(BizInboundTask.SUPPLY_WORK_TYPE);
        bizInboundTask.setContainerNo(containerTask.getContainerNo());
        bizInboundTask.setStatus(BaseStatusEnum.STATUS_INIT.getCode());
        bizInboundTask.setCreateTime(LocalDateTime.now());
        bizInboundTask.setUpdateTime(LocalDateTime.now());
        bizInboundTask.setExtend2(containerTask.getId() + "");
        bizInboundTask.setOriginalLocation("B");
        BizInboundTaskDetail bizInboundTaskDetail = new BizInboundTaskDetail();
        bizInboundTaskDetail.setContainerSubNo(containerTask.getContainerNo());
        bizInboundTaskDetail.setCreateTime(LocalDateTime.now());
        bizInboundTaskDetail.setUpdateTime(LocalDateTime.now());
        bizInboundTaskDetail.setQty(reduce);
        bizInboundTaskDetail.setOwnerId(containerTask.getOwnerId());
        bizInboundTaskDetail.setItemId(containerTask.getItemId());
        bizInboundTaskDetail.setLotId(containerTask.getLotId());
        bizInboundTaskDetail.setPackId(containerTask.getPackId());
//        bizInboundTask.setSubList(Arrays.asList(bizInboundTaskDetail));
        bizInboundTaskMapper.save(bizInboundTask);
        bizInboundTaskDetail.setInboundId(bizInboundTask.getId());
        bizInboundTaskDetailMapper.saveBatch(Arrays.asList(bizInboundTaskDetail));
//        AssertUtils.isTrue(dismantleFeignService.callShelves(bizInboundTask),"补货出库没有空闲站台");
        return SUPPLY_FLAG_NO_CARRY;
    }

    private CarryTask setCarryTask(ContainerTask containerTask, String endRegion, String businessProperty, String businessType) throws Exception {
        CarryTask carryTask = new CarryTask();
        carryTask.setTaskType(LocationConstants.PATH_TASK_TYPE_CARRY);
        carryTask.setId(PrologStringUtils.newGUID());
        carryTask.setContainerNo(containerTask.getContainerNo());
        carryTask.setPalletNo(containerTask.getContainerNo());
        ContainerLocation containerLocation = plgFeignRouteService.findLocationByContainerList(Arrays.asList(containerTask.getContainerNo())).stream().findFirst().orElseThrow(() -> new PrologException("未查询到容器点位信息"));
        carryTask.setStartRegion(containerLocation.getCurrentArea());
        carryTask.setStartLocation(containerLocation.getCurrentLocation());
        carryTask.setEndRegion(endRegion);
        carryTask.setHeight(1);
        carryTask.setBusinessProperty(businessProperty);
        carryTask.setBusinessType(String.valueOf(businessType));
        carryTask.setBusinessDescribe("补货出库搬运");
        carryTask.setPriority(99);
        carryTask.setExtend3(containerTask.getId() + "");
        return carryTask;
    }

    @Override
    public void wmsOutCallBack() throws Exception {
        Criteria criteria = Criteria.forClass(ContainerTask.class);
        criteria.setRestriction(Restrictions.eq("status", ContainerTaskStatusConstants.IN_PLACE_STATUS));
        criteria.setOrder(Order.newInstance().asc("finishTime"));
        List<ContainerTask> containerTaskList = containerTaskMapper.findByCriteria(criteria);
        if (CollectionUtil.isEmpty(containerTaskList)) {
            log.info("无回告的出库托盘任务");
            return;
        }
        for (ContainerTask containerTask : containerTaskList) {
            BizOutboundTask bizOutboundTask = bizOutboundTaskMapper.findById(containerTask.getOutboundId(), BizOutboundTask.class);
            if (ObjectUtils.isNull(bizOutboundTask)) {
                throw new PrologException(String.format("通过主键Id【%s】未查询到出库任务", containerTask.getOutboundId()));
            }

            BizOutboundTaskDetail bizOutboundTaskDetail = bizOutboundTaskDetailMapper.findById(containerTask.getOutboundDetailId(), BizOutboundTaskDetail.class);
            if (ObjectUtils.isNull(bizOutboundTask)) {
                throw new PrologException(String.format("通过主键Id【%s】未查询到出库任务明细", containerTask.getOutboundDetailId()));
            }

            containerTask.setStatus(ContainerTaskStatusConstants.FINISH_STATUS);
            containerTask.setUpdateTime(LocalDateTime.now());
            containerTaskMapper.update(containerTask);


            //托盘出库完成
            containerInfoMapper.deleteByMap(MapUtils.put("containerNo", containerTask.getContainerNo()).getMap(), ContainerInfo.class);
            containerInfoDetailMapper.deleteByMap(MapUtils.put("containerNo", containerTask.getContainerNo()).getMap(), ContainerInfoDetail.class);

            //修改当前详情表
            criteria = new Criteria(ContainerTask.class);
            criteria.setRestriction(Restrictions.lt("status", ContainerTaskStatusConstants.IN_PLACE_STATUS).and(Restrictions.eq("outboundDetailId", containerTask.getOutboundDetailId())));
            Long count = containerTaskMapper.findCountByCriteria(criteria);
            if (count == 0) {
                BizOutboundTaskDetailHis bizOutboundTaskDetailHis = new BizOutboundTaskDetailHis();
                BeanUtil.copyProperties(bizOutboundTaskDetail, bizOutboundTaskDetailHis);
                bizOutboundTaskDetailMapper.deleteById(bizOutboundTaskDetail.getId(), BizOutboundTaskDetail.class);
                bizOutboundTaskDetailHisMapper.save(bizOutboundTaskDetailHis);
            }
            //修改主表
            criteria = new Criteria(BizOutboundTaskDetail.class);
            criteria.setRestriction(Restrictions.lt("status", OrderStatusConstants.FINISH_STATUS).and(Restrictions.eq("outboundId", containerTask.getOutboundId())));
            count = bizOutboundTaskDetailMapper.findCountByCriteria(criteria);
            if (count == 0) {
                BizOutboundTaskHis bizOutboundTaskHis = new BizOutboundTaskHis();
                BeanUtil.copyProperties(bizOutboundTask, bizOutboundTaskHis);
                bizOutboundTaskMapper.deleteById(bizOutboundTask.getId(), BizOutboundTask.class);
                bizOutboundTaskHisMapper.save(bizOutboundTaskHis);
            }

            //添加变更记录
            ContainerStoreChange containerStoreChange = new ContainerStoreChange();
            containerStoreChange.setContainerNo(containerTask.getContainerNo());
            containerStoreChange.setContainerSubNo(containerTask.getContainerNo());
            containerStoreChange.setTaskType(BaseTypeEnum.OUTBOUND_TYPE.getCode());
            containerStoreChange.setSoureQty(containerTask.getQty());
            containerStoreChange.setChangeQty(containerTask.getQty());
            containerStoreChange.setTargetQty(new BigDecimal("0"));
            containerStoreChange.setUpperSystemId(bizOutboundTask.getBillNo());
            containerStoreChange.setRemark("出库完成");
            containerStoreChange.setPackId(containerTask.getPackId());
            containerStoreChange.setOwnerId(containerTask.getOwnerId());
            containerStoreChange.setLotId(containerTask.getLotId());
            containerStoreChange.setItemId(containerTask.getItemId());
            containerStoreChange.setCreateTime(LocalDateTime.now());
            containerStoreChange.setUpdateTime(LocalDateTime.now());
            containerStoreChangeMapper.save(containerStoreChange);

            List<WmsOutCbReqDto> wmsOutCbReqDtos = setWmsOutBackReqDto(bizOutboundTask, bizOutboundTaskDetail, containerTask);

            try {
                RestMessage<Object> wmsResult = wmsOutBackService.execute(wmsOutCbReqDtos);
                log.info("出库完成回告wms" + wmsResult);
            } catch (RetryException e) {
                log.warn("出库完成回告wms失败，进入重试：" + e.getMessage());
            }


        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doOutboundCallBack(CarryTaskCallbackDto carryTaskCallbackDto) throws Exception {
        // 查询业务类型
        Criteria criteria = new Criteria(ContainerTask.class);
        criteria.setRestriction(Restrictions.lt("status", ContainerTaskStatusConstants.IN_PLACE_STATUS).and(Restrictions.eq("containerNo", carryTaskCallbackDto.getContainerNo())));
        List<ContainerTask> containerTaskList = containerTaskMapper.findByCriteria(criteria);
        if (CollectionUtil.isEmpty(containerTaskList)) {
            throw new PrologException(String.format("托盘【%s】没有容器任务", carryTaskCallbackDto.getContainerNo()));
        }
        //修改状态为到位
        containerTaskMapper.updateMapByCriteria(MapUtils.put("status", ContainerTaskStatusConstants.IN_PLACE_STATUS).put("finishTime", LocalDateTime.now()).getMap(), criteria);

        //追加托盘修改逻辑
        addPalletLogic(containerTaskList);

        //拆零库区不回告wms到位
        if (containerTaskList.get(0).getWmsLocationCode().equals(WmsConstants.AGVL)) {
            return;
        }
        //补货出库不回告wms
        if (containerTaskList.get(0).getTaskType() == BaseBillTypeConstants.REPLENISH_TYPE) {
            LocationCfg locationCfg = locationCfgService.findOneByMap(MapUtils.put("type", LocationCfgConstants.AGVL_TP).put("wmsAreaNo", WmsCarryTaskInCode.AGVL.getCode()).getMap());
            //补货出库搬运到拆零托盘库 修改库存库区
            if (carryTaskCallbackDto.getEndRegion().equals(locationCfg.getAreaNo())) {
                Criteria containerInfoCr = new Criteria(ContainerInfo.class);
                containerInfoCr.restrictionBuilder().eq(ContainerInfo::getContainerNo, carryTaskCallbackDto.getContainerNo()).build();
                Criteria containerInfoDetailCr = new Criteria(ContainerInfoDetail.class);
                containerInfoDetailCr.restrictionBuilder().eq(ContainerInfoDetail::getContainerNo, carryTaskCallbackDto.getContainerNo()).build();
                containerInfoMapper.updateMapByCriteria(MapUtils.put("areaType", 5).put("warehouseArea", WmsCarryTaskInCode.AGVL.getCode()).getMap(), containerInfoCr);
                containerInfoDetailMapper.updateMapByCriteria(MapUtils.put("areaType", 5).getMap(), containerInfoDetailCr);
            }
            return;
        }

        List<Integer> outboundIds = containerTaskList.stream().map(ContainerTask::getOutboundId).collect(Collectors.toList());
        List<Integer> outboundDetailIds = containerTaskList.stream().map(ContainerTask::getOutboundDetailId).collect(Collectors.toList());
        List<BizOutboundTask> bizOutboundTasks = bizOutboundTaskMapper.findByIds(BizOutboundTask.class, outboundIds.toArray(), null);
        if (CollectionUtils.isEmpty(bizOutboundTasks)) {
            throw new PrologException(String.format("通过主键Id【%s】未查询到出库任务", containerTaskList.stream().map(ContainerTask::getOutboundId).map(Objects::toString).collect(Collectors.joining(","))));
        }

        List<BizOutboundTaskDetail> bizOutboundTaskDetails = bizOutboundTaskDetailMapper.findByIds(BizOutboundTaskDetail.class, outboundDetailIds.toArray(), null);
        if (ObjectUtils.isNull(bizOutboundTaskDetails)) {
            throw new PrologException(String.format("通过主键Id【%s】未查询到出库任务明细", containerTaskList.stream().map(ContainerTask::getOutboundDetailId).map(Objects::toString).collect(Collectors.joining(","))));
        }
        //回告wms
        List<WmsOutCbReqDto> wmsOutCbReqDtos = setWmsOutBackReqDto(containerTaskList, bizOutboundTasks, bizOutboundTaskDetails);

        try {
            RestMessage<Object> wmsResult = wmsOutBackService.execute(wmsOutCbReqDtos);
            log.info("出库到位回告wms" + wmsResult);
        } catch (RetryException e) {
            log.warn("出库到位回告wms失败，进入重试：" + e.getMessage());
        }
    }


    /**
     * 拆零库区到位回告
     *
     * @param carryTaskCallbackDto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doDismantleOutboundCallBack(CarryTaskCallbackDto carryTaskCallbackDto) throws Exception {
        //根据终点区域判断是零发整站台或者拆零站台
        List<StationCfg> stationList = stationCfgService.findByMap(null);
        StationCfg stationCfg = stationList.stream().filter(o -> o.getAreaNo().equals(carryTaskCallbackDto.getEndRegion())).findFirst().orElse(null);
        AssertUtils.isNotTrue(stationCfg == null, "拆零区到位站台未配置");
        Integer type = stationCfg.getStationType().equals(StationTypeEnum.LFZ_TYPE.getCode()) ? 1 : 2;
        //查询对应的容器任务
        List<ContainerTaskDismantleDto> containerTaskDismantleDtoList = containerTaskMapper.findContainerTask(carryTaskCallbackDto.getContainerNo(), type);
        if (CollectionUtils.isEmpty(containerTaskDismantleDtoList)) {
            return;
        }

        EfficiencyStatistics efficiencyStatistics = new EfficiencyStatistics();
        efficiencyStatistics.setContainerNo(carryTaskCallbackDto.getContainerNo());
        efficiencyStatistics.setEisWaveNo(
                containerTaskDismantleDtoList.stream().min(Comparator.comparing(ContainerTaskDismantleDto::getEisWaveNo)).get().getEisWaveNo()
        );
        efficiencyStatistics.setOrderCount(containerTaskDismantleDtoList.size());
        efficiencyStatistics.setOrderList(containerTaskDismantleDtoList.stream().map(ContainerTaskDismantleDto::getOutboundId).distinct().collect(Collectors.toList()).toString());
        efficiencyStatistics.setOrderDetailList(containerTaskDismantleDtoList.stream().map(ContainerTaskDismantleDto::getOutboundDetailId).distinct().collect(Collectors.toList()).toString());
        efficiencyStatistics.setArrivalTime(new Date());
        efficiencyStatistics.setBreedCount((int) containerTaskDismantleDtoList.stream().map(ContainerTaskDismantleDto::getItemId).distinct().count());
        efficiencyStatisticsMapper.save(efficiencyStatistics);

        Criteria criteria = new Criteria(ContainerTaskDismantle.class);
        criteria.setRestriction(Restrictions.in("id", containerTaskDismantleDtoList.stream().map(ContainerTaskDismantleDto::getId).collect(Collectors.toList()).toArray()));
        containerTaskDismantleMapper.updateMapByCriteria(MapUtils.put("status", ContainerTaskStatusConstants.IN_PLACE_STATUS).getMap(), criteria);
        //追加托盘修改逻辑
        addDismantlePalletLogic(containerTaskDismantleDtoList);

        List<PalletInfo> palletInfos = palletInfoMapper.findByMap(MapUtils.put("palletNo", carryTaskCallbackDto.getContainerNo()).getMap(), PalletInfo.class);
        if (!CollectionUtils.isEmpty(palletInfos)) {
            PalletInfo palletInfo = palletInfos.get(0);
            palletInfo.setStatus(0);
            palletInfoMapper.update(palletInfo);
        }

        Criteria criteria1 = new Criteria(ContainerInfo.class);
        criteria1.restrictionBuilder().eq(ContainerInfo::getContainerNo, carryTaskCallbackDto.getContainerNo()).build();
        containerInfoService.updateMapByCriteria(MapUtils.put("taskType", BaseTaskTypeConstants.DISMANTLE_OUTBOUND_END_TYPE).put("updateTime", LocalDateTime.now()).getMap(), criteria1);
    }

    private void addDismantlePalletLogic(List<ContainerTaskDismantleDto> containerTaskDismantleDtoList) throws Exception {
        for (ContainerTaskDismantleDto containerTaskDismantleDto : containerTaskDismantleDtoList) {
            Criteria criteria = new Criteria(ContainerTaskDismantle.class);
            criteria.setRestriction(Restrictions.eq("containerTaskId", containerTaskDismantleDto.getContainerTaskId()).and(Restrictions.lt("status", ContainerTaskStatusConstants.IN_PLACE_STATUS)));
            long count = containerTaskDismantleMapper.findCountByCriteria(criteria);
            if (count == 0) {
                containerTaskService.updateMapById(containerTaskDismantleDto.getContainerTaskId(), MapUtils.put("status", ContainerTaskStatusConstants.IN_PLACE_STATUS).getMap());
            }
            updateOutTaskStatus(containerTaskDismantleDto.getOutboundId(), containerTaskDismantleDto.getOutboundDetailId());
        }
    }

    private void updateOutTaskStatus(Integer outboundId, Integer outboundDetailId) throws Exception {
        //判断任务明细是否完成
        Criteria criteria = new Criteria(ContainerTask.class);
        criteria.setRestriction(Restrictions.eq("outboundDetailId", outboundDetailId).and(Restrictions.lt("status", ContainerTaskStatusConstants.IN_PLACE_STATUS)));
        long count = containerTaskMapper.findCountByCriteria(criteria);
        if (count == 0) {
            BizOutboundTaskDetail outTaskDetail = bizOutboundTaskDetailMapper.findById(outboundDetailId, BizOutboundTaskDetail.class);
            if (outTaskDetail == null) {
                throw new Exception("出库任务明细不存在，任务ID：" + outboundDetailId);
            }
            if (outTaskDetail.getStatus() < BaseStatusEnum.STATUS_CARRY.getCode()) {
                outTaskDetail.setActQty(outTaskDetail.getPlanQty());
                outTaskDetail.setStatus(BaseStatusEnum.STATUS_CARRY.getCode());
                outTaskDetail.setUpdateTime(LocalDateTime.now());
                bizOutboundTaskDetailMapper.update(outTaskDetail);
            }
        }
        //判断主任务是否完成
        criteria = new Criteria(BizOutboundTaskDetail.class);
        criteria.setRestriction(Restrictions.eq("outboundId", outboundId).and(Restrictions.lt("status", BaseStatusEnum.STATUS_CARRY.getCode())));
        count = bizOutboundTaskDetailMapper.findCountByCriteria(criteria);
        if (count == 0) {
            BizOutboundTask outTask = bizOutboundTaskMapper.findById(outboundId, BizOutboundTask.class);
            if (outTask == null) {
                throw new Exception("出库任务不存在，任务ID：" + outboundId);
            }
            if (outTask.getStatus() < BaseStatusEnum.STATUS_CARRY.getCode()) {
                outTask.setStatus(BaseStatusEnum.STATUS_CARRY.getCode());
                outTask.setUpdateTime(LocalDateTime.now());
                bizOutboundTaskMapper.update(outTask);
            }
        }


    }

    private void addPalletLogic(List<ContainerTask> containerTaskList) throws Exception {
        for (ContainerTask containerTask : containerTaskList) {
            //判断任务明细是否完成
            updateOutTaskStatus(containerTask.getOutboundId(), containerTask.getOutboundDetailId());
        }
    }


    private List<WmsOutCbReqDto> setWmsOutBackReqDto(List<ContainerTask> containerTaskList, List<BizOutboundTask> bizOutboundTasks, List<BizOutboundTaskDetail> bizOutboundTaskDetails) {
        Map<Integer, List<BizOutboundTask>> outboundTaskMap = bizOutboundTasks.stream().collect(Collectors.groupingBy(BizOutboundTask::getId, Collectors.toList()));
        Map<Integer, List<BizOutboundTaskDetail>> outboundTaskDetailMap = bizOutboundTaskDetails.stream().collect(Collectors.groupingBy(BizOutboundTaskDetail::getId, Collectors.toList()));
        List<WmsOutDetailCbReqDto> wmsOutBackDetailReqDtoList = new ArrayList<>();
        for (ContainerTask containerTask : containerTaskList) {
            BizOutboundTask outboundTask = outboundTaskMap.get(containerTask.getOutboundId()).get(0);
            if (ObjectUtils.isNull(outboundTask)) {
                throw new PrologException(String.format("通过主键Id【%s】未查询到出库任务", containerTask.getOutboundId()));
            }
            BizOutboundTaskDetail outboundTaskDetail = outboundTaskDetailMap.get(containerTask.getOutboundDetailId()).get(0);
            if (ObjectUtils.isNull(outboundTaskDetail)) {
                throw new PrologException(String.format("通过主键Id【%s】未查询到出库任务明细", containerTask.getOutboundDetailId()));
            }
            WmsOutDetailCbReqDto WmsOutBackDetailReqDto = setWmsOutBackDetailReqDto(outboundTask, outboundTaskDetail, containerTask);
            wmsOutBackDetailReqDtoList.add(WmsOutBackDetailReqDto);
        }
        WmsOutCbReqDto wmsOutBackReqDto = new WmsOutCbReqDto();
        wmsOutBackReqDto.setContainerNo(containerTaskList.get(0).getContainerNo());
        wmsOutBackReqDto.setMsgId(StrUtil.uuid());
        wmsOutBackReqDto.setSubList(wmsOutBackDetailReqDtoList);
        return Arrays.asList(wmsOutBackReqDto);
    }


    private WmsOutDetailCbReqDto setWmsOutBackDetailReqDto(BizOutboundTask bizOutboundTask, BizOutboundTaskDetail bizOutboundTaskDetail, ContainerTask containerTask) {
        WmsOutDetailCbReqDto entity = new WmsOutDetailCbReqDto();
        entity.setBillNo(bizOutboundTask.getBillNo());
        entity.setBusinessType(bizOutboundTask.getBusinessType());
        entity.setItemId(containerTask.getItemId());
        entity.setOwnerId(containerTask.getOwnerId());
        entity.setLotId(containerTask.getLotId());
        entity.setPackId(containerTask.getPackId());
        entity.setQty(containerTask.getQty());
        entity.setTaskId(bizOutboundTaskDetail.getExtend1());
        entity.setEisTaskId(PrologStringUtils.newGUID());
        entity.setEnterpriseId(bizOutboundTask.getEnterpriseId());
        entity.setWarehouseId(bizOutboundTask.getWmsWarehouseId());
        entity.setWorkType(bizOutboundTask.getWorkType());
        entity.setTaskLineId(bizOutboundTaskDetail.getSeqNo());
        entity.setWmsLocationCode(bizOutboundTask.getWmsLocationCode());
        entity.setTargetArea(bizOutboundTaskDetail.getTargetArea());
        entity.setTargetLocation(bizOutboundTaskDetail.getTargetArea());
        return entity;
    }

    /**
     * 出库组单
     * 1.查同波次出库任务订单  状态0  类型  出库
     * 2.查询有无同波次  同产成品的任务包
     * 2.1.无  生成任务包
     * 2.2.有 查询任务包当前有无回告pms拣选信息
     * 2.3有   生成任务包
     * 2.4无 在途追加
     * 3.更新出库任务状态
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void composeOutTaskPackage() throws Exception {
        int packageSwitch = Integer.parseInt(String.valueOf(redisTemplate.opsForValue().get(SysParamConstants.SYSPRE_FIX + SysParamConstants.ASSEMBLY_PACKAGE_SWITCH)));

        if (packageSwitch == 0) {
            log.info("组单开关关闭，暂停组单");
            return;
        }
        Criteria criteria = new Criteria(BizOutboundTask.class);
        criteria.setRestriction(Restrictions.eq("status", 0).and(Restrictions.in("billType", new Integer[]{10, 20})).and(Restrictions.eq("checkStore", 1)));
        criteria.setOrder(Order.newInstance().desc("priority").asc("createTime"));
        //查询待出库订单
        List<BizOutboundTask> outList = bizOutboundTaskMapper.findByCriteria(criteria);
        if (CollectionUtils.isEmpty(outList)) {
            log.debug("暂无待组单的订单");
            return;
        }

        //可以组单的最大的订单数
        int taskPackageNum = Integer.parseInt(String.valueOf(redisTemplate.opsForValue().get(SysParamConstants.SYSPRE_FIX + SysParamConstants.TASK_PACKAGE_NUM)));
        for (BizOutboundTask task : outList) {
            try {
                composeOutTaskPackageLogic(task, taskPackageNum);
            } catch (Exception e) {
                log.info(String.format("订单：[%s]组任务包失败，原因为[%s]", task.getBillNo(), e.getMessage()));
            }
        }


    }

    private void composeOutTaskPackageLogic(BizOutboundTask task, int taskPackageNum) {
        Criteria criteria = new Criteria(PickingOrder.class);
        criteria.setRestriction(Restrictions.lt("status", 20).and(Restrictions.lt("billNum", taskPackageNum)).and(Restrictions.eq("waveNo", task.getWaveNo()))
                //.and(Restrictions.eq("productCode",task.getProductCode()))
                .and(Restrictions.eq("pickingType", task.getBillType())));
        criteria.setOrder(Order.newInstance().asc("createTime"));
        List<PickingOrder> pickList = pickingOrderService.findByCriteria(criteria);
        PickingOrder pickingOrder = null;
        if (!CollectionUtils.isEmpty(pickList)) {
            //存在同波次同产成品的任务包
            pickingOrder = pickList.get(0);
            pickingOrder.setBillNum(pickingOrder.getBillNum() + 1);
            pickingOrderService.update(pickingOrder);
        } else {
            //不存在同波次同产成品的任务包
            pickingOrder = new PickingOrder();
            pickingOrder.setPickingOrderNo(PrologStringUtils.newGUID());
            pickingOrder.setStatus(0);
            pickingOrder.setPickingMode(0);
            pickingOrder.setPickingType(task.getBillType());
            pickingOrder.setCreateTime(LocalDateTime.now());
            //pickingOrder.setProductCode(task.getProductCode());
            //pickingOrder.setProductName(task.getProductName());
            pickingOrder.setWaveNo(task.getWaveNo());
            pickingOrder.setBillNum(1);
            pickingOrder.setIsLock(0);
            pickingOrderService.save(pickingOrder);
        }

        bizOutboundTaskMapper.updateMapById(task.getId(), MapUtils.put("status", 1).put("pickOrderId", pickingOrder.getId()).put("updateTime", LocalDateTime.now()).getMap(), BizOutboundTask.class);
    }

    @Override
    public void checkStock() throws Exception {
        //按库区检验订单
        List<LocationCfg> locationCfgList = locationCfgService.findByMap(MapUtils.put("type", LocationCfgConstants.EISKQ).getMap());
        AssertUtils.isNotTrue(CollectionUtils.isEmpty(locationCfgList), "locationCfg库区未配置");
        locationCfgList = locationCfgList.stream().filter(o -> !o.getWmsAreaNo().equals(WmsConstants.AGVL)).collect(Collectors.toList());
        //查所有订单
        List<OrderDto> orderList = getOutboundTaskByWarehouseArea();
        AssertUtils.isNotTrue(CollectionUtils.isEmpty(orderList), "检验库存，无可校验的订单库存");
        //查所有运行中的任务
        List<ContainerTask> containerTaskList = containerTaskService.findAll();
        //查所有可用库存
        List<ContainerInfoDto> containerInfoDtoList = containerInfoService.getAllStock(ContainerInfoTypeConstants.GOODS_PALLET);

        //根据库区校验订单库存
        for (LocationCfg location : locationCfgList) {
            try {
                //过滤当前库区的出库任务
                List<OrderDto> orderTempList = orderList.stream().filter(o -> o.getWmsLocationCode().equals(location.getWmsAreaNo())).collect(Collectors.toList());
                if (CollectionUtils.isEmpty(orderTempList)) {
                    log.info(String.format("库区[%s]，无待出库的订单任务", location.getWmsAreaNo()));
                    continue;
                }
                //过滤当前库区已出库托盘
                List<ContainerTask> taskTempList = containerTaskList.stream().filter(s -> s.getWmsLocationCode().equals(location.getWmsAreaNo())).collect(Collectors.toList());
                //过滤当前库区的库存
                List<ContainerInfoDto> stockTempList = containerInfoDtoList.stream().filter(s -> s.getWarehouseArea().equals(location.getWmsAreaNo())).collect(Collectors.toList());

                //查出当前库区所有整托出库任务的子托盘号集合
                List<String> full = taskTempList.stream().filter(s -> s.getWmsLocationCode().equals(location.getWmsAreaNo()) && (s.getContainerType() == ContainerTaskStatusConstants.ONE_FULL_TRAY_TYPE || s.getContainerType() == ContainerTaskStatusConstants.MORE_FULL_TRAY_TYPE)).map(ContainerTask::getContainerSubNo).distinct().collect(Collectors.toList());
                //过滤正在任务中的整托托盘
                stockTempList = stockTempList.stream().filter(containerInfoDto -> !full.contains(containerInfoDto.getContainerSubNo())).collect(Collectors.toList());
                //根据子托盘集合去除拼托的库存
                Map<String, BigDecimal> collect = taskTempList.stream().collect(Collectors.toMap(ContainerTask::getContainerSubNo, ContainerTask::getQty, BigDecimal::add));
                for (ContainerInfoDto containerInfoDto : stockTempList) {
                    BigDecimal qty = collect.get(containerInfoDto.getContainerSubNo());
                    if (Objects.nonNull(qty)) {
                        containerInfoDto.setQty(containerInfoDto.getQty().subtract(qty));
                    }
                }
                Map<String, BigDecimal> storeMap = stockTempList.stream().collect(Collectors.toMap(o -> o.getBusinessProperty(), ContainerInfoDto::getQty, (k1, k2) -> k1.add(k2)));

                for (OrderDto orderDto : orderTempList) {
                    //盘点不校验库存
                    BizOutboundTask outboundTask = bizOutboundTaskMapper.findById(orderDto.getOrderId(), BizOutboundTask.class);
                    if (orderDto.getWorkType().equals("40")) {
                        outboundTask.setErrorMsg(null);
                        outboundTask.setCheckStore(1);
                        outboundTask.setUpdateTime(LocalDateTime.now());
                        bizOutboundTaskMapper.update(outboundTask);
                        Criteria criteria = new Criteria(BizOutboundTaskDetail.class);
                        criteria.setRestriction(Restrictions.eq("outboundId", outboundTask.getId()).and(Restrictions.eq("checkStore", 0)));
                        bizOutboundTaskDetailMapper.updateMapByCriteria(MapUtils.put("checkStore", 1).getMap(), criteria);
                        continue;
                    }
                    HashMap<Integer, BigDecimal> map = new HashMap<>();
                    List<OrderDetailDto> orderDetailDtoList = orderDto.getOrderDetailDtoList();
                    for (OrderDetailDto detailDto : orderDetailDtoList) {
                        if (ObjectUtil.isNotEmpty(detailDto.getContainerNo())) {
                            ContainerInfoDto containerInfoDto = stockTempList.stream().filter(x -> x.getContainerSubNo().equals(detailDto.getContainerNo())).findFirst().orElse(null);
                            boolean present = Optional.ofNullable(containerInfoDto).isPresent();
                            if (!present) {
                                map.put(detailDto.getOrderDetailId(), BigDecimal.ZERO);
                                log.warn("订单明细id【{}】所需库存不足，库内不存在此托盘【{}】", detailDto.getOrderDetailId(), detailDto.getContainerNo());
                            }
                            continue;
                        }
                        BigDecimal storeQty = storeMap.get(detailDto.getBusinessProperty());
                        if (Objects.isNull(storeQty)) {
                            map.put(detailDto.getOrderDetailId(), detailDto.getPlanNum());
                            log.warn("订单明细id【{}】所需库存【{}】不足，可用库存为0", detailDto.getOrderDetailId(), detailDto.getPlanNum());
                            continue;
                        }
                        if (storeQty.compareTo(detailDto.getPlanNum()) < 0) {
                            map.put(detailDto.getOrderDetailId(), detailDto.getPlanNum().subtract(storeQty));
                            //storeMap.remove(detailDto.getBusinessProperty());
                            log.warn("订单明细id【{}】所需库存【{}】不足，可用库存为{}", detailDto.getOrderDetailId(), detailDto.getPlanNum(), storeQty);
                            continue;
                        }
                        if (storeQty.compareTo(detailDto.getPlanNum()) == 0) {
                            storeMap.remove(detailDto.getBusinessProperty());
                        } else {
                            storeMap.put(detailDto.getBusinessProperty(), storeQty.subtract(detailDto.getPlanNum()));
                        }
                    }

                    if (map.size() > 0) {
                        //outboundTask.setStatus(BaseStatusEnum.STATUS_NO_STOCK.getCode());
                        outboundTask.setErrorMsg("库存不足");
                    } else {
                        //outboundTask.setStatus(BaseStatusEnum.STATUS_INIT.getCode());
                        outboundTask.setErrorMsg(null);
                        outboundTask.setCheckStore(1);

                        Criteria criteria = new Criteria(BizOutboundTaskDetail.class);
                        criteria.setRestriction(Restrictions.eq("outboundId", outboundTask.getId()).and(Restrictions.eq("checkStore", 0)));
                        bizOutboundTaskDetailMapper.updateMapByCriteria(MapUtils.put("checkStore", 1).getMap(), criteria);
                    }
                    outboundTask.setUpdateTime(LocalDateTime.now());
                    bizOutboundTaskMapper.update(outboundTask);
                }
            } catch (Exception e) {
                log.warn(String.format("校验库存：【%s】库区校验库存失败，原因为：【%s】", location.getWmsAreaNo(), e.getMessage()));
            }
        }

    }


    public List<OrderDto> getOutboundTaskByWarehouseArea() throws Exception {
        List<OrderDto> orderDtoList = bizOutboundTaskMapper.getOutboundTaskByArea();
        if (orderDtoList.isEmpty()) {
            return null;
        }
        List<OrderDetailDto> orderDetailDtoList = bizOutboundTaskDetailMapper.findDetailList();
        if (orderDetailDtoList.isEmpty()) {
            return null;
        }
        Map<Integer, List<OrderDetailDto>> collect = orderDetailDtoList.stream().collect(Collectors.groupingBy(OrderDetailDto::getOrderId));

        Iterator<OrderDto> iterator = orderDtoList.iterator();
        while (iterator.hasNext()) {
            OrderDto orderDto = iterator.next();
            if (!collect.containsKey(orderDto.getOrderId())) {
                iterator.remove();
            }
            orderDto.setOrderDetailDtoList(collect.get(orderDto.getOrderId()));
        }
        return orderDtoList;
    }

    /**
     * 找托盘
     *
     * @param waitTask
     */
    @Override
    public void saveContainerTask(BizOutboundTask waitTask) {


    }

    private List<WmsOutCbReqDto> setWmsOutBackReqDto(BizOutboundTask bizOutboundTask, BizOutboundTaskDetail bizOutboundTaskDetail, ContainerTask containerTask) {
        WmsOutCbReqDto wmsOutCbReqDto = new WmsOutCbReqDto();
        wmsOutCbReqDto.setMsgId(StrUtil.uuid());
        wmsOutCbReqDto.setContainerNo(containerTask.getContainerNo());
        wmsOutCbReqDto.setWholeFlag("1");
        WmsOutDetailCbReqDto entity = new WmsOutDetailCbReqDto();
        entity.setBillNo(bizOutboundTask.getBillNo());
        entity.setBusinessType(bizOutboundTask.getBusinessType());
        entity.setItemId(containerTask.getItemId());
        entity.setOwnerId(containerTask.getOwnerId());
        entity.setLotId(containerTask.getLotId());
        entity.setPackId(containerTask.getPackId());
        entity.setQty(containerTask.getQty());
        entity.setTaskId(bizOutboundTask.getUpperSystemTaskId());
        entity.setWarehouseId(bizOutboundTask.getWmsWarehouseId());
        entity.setEnterpriseId(bizOutboundTask.getEnterpriseId());
        entity.setWorkType(bizOutboundTask.getWorkType());
        entity.setTaskLineId(bizOutboundTaskDetail.getSeqNo());
        entity.setWmsLocationCode(bizOutboundTask.getWmsLocationCode());
//        entity.setTargetArea(bizOutboundTask.getTargetArea());
//        entity.setTargetLocation(bizOutboundTask.getTargetLocation());
        wmsOutCbReqDto.setSubList(Arrays.asList(entity));
        return Arrays.asList(wmsOutCbReqDto);
    }

    @Override
    public void mergeOutBack(CarryTaskCallbackDto carryTaskCallbackDto) {
        ContainerInfo containerInfo = containerInfoService.findOneByContainerNo(carryTaskCallbackDto.getContainerNo());
        containerInfoService.updateTaskType(containerInfo.getId(), ContainerTaskTypeConstants.MERGE_OUT_ARR);
    }

    @Override
    public void doDismantleInCacheCallBack(CarryTaskCallbackDto carryTaskCallbackDto) {
        Criteria criteria = new Criteria(BizInboundTask.class);
        criteria.restrictionBuilder().eq(BizInboundTask::getContainerNo, carryTaskCallbackDto.getContainerNo()).build();
        bizInboundTaskMapper.updateMapByCriteria(MapUtils.put("status", 15).put("updateTime", LocalDateTime.now()).getMap(), criteria);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TxTransaction(rollbackFor = Exception.class)
    public void doGenTask(ContainerTask containerTask, List<BizOutboundTask> outTaskList, List<ContainerTask> containerTasks, List<String> sendTask) throws Exception {
        //判断如果当前的托盘为拼托 查有无追加的托盘为多订单整托的（针对追加成整托的托盘发送去整托站台（已发送的任务改变不了目标区域））
        updateContainerType(containerTask);
        BizOutboundTask outboundTask = outTaskList.stream().filter(item -> Objects.equals(item.getId(), containerTask.getOutboundId())).findFirst().get();
        this.supplyTask(containerTask, outboundTask, containerTasks);

        Criteria criteria = new Criteria(ContainerInfo.class);
        criteria.setRestriction(Restrictions.eq("containerNo", containerTask.getContainerNo()));
        containerInfoService.updateByMap(MapUtils.put("taskType", BaseTaskTypeConstants.OUTBOUND_TYPE).getMap(), criteria);

        containerTaskService.updateStatusBatch(containerTasks, ContainerTaskStatusConstants.START_STATUS);

        doUpdateOutTask(containerTasks);
        sendTask.add(containerTask.getContainerNo());
    }

    private void doUpdateOutTask(List<ContainerTask> collectList) throws Exception {
        for (ContainerTask containerTask : collectList) {
            BizOutboundTaskDetail outTaskDetail = bizOutboundTaskDetailMapper.findById(containerTask.getOutboundDetailId(), BizOutboundTaskDetail.class);
            if (outTaskDetail == null) {
                throw new Exception("出库任务明细不存在，任务ID：" + containerTask.getOutboundDetailId());
            }
            //更新出库任务状态
            outTaskDetail.setActQty(new BigDecimal(outTaskDetail.getActQty() + "").add(new BigDecimal(containerTask.getQty() + "")));
            outTaskDetail.setUpdateTime(LocalDateTime.now());
            if (outTaskDetail.getActQty().compareTo(outTaskDetail.getPlanQty()) >= 0) {
                outTaskDetail.setStatus(BaseStatusEnum.STATUS_CARRY.getCode());
                bizOutboundTaskDetailMapper.update(outTaskDetail);
                List<BizOutboundTaskDetail> bizOutboundTaskDetailList = bizOutboundTaskDetailMapper.findByMap(MapUtils.put("outboundId", outTaskDetail.getOutboundId()).getMap(), BizOutboundTaskDetail.class);
                long count = bizOutboundTaskDetailList.stream().filter(item -> item.getStatus() < BaseStatusEnum.STATUS_CARRY.getCode()).count();
                if (count == 0) {
                    BizOutboundTask outboundTask = bizOutboundTaskMapper.findById(outTaskDetail.getOutboundId(), BizOutboundTask.class);
                    outboundTask.setStatus(BaseStatusEnum.STATUS_CARRY.getCode());
                    outboundTask.setUpdateTime(LocalDateTime.now());
                    bizOutboundTaskMapper.update(outboundTask);
                }
            } else {
                bizOutboundTaskDetailMapper.update(outTaskDetail);
            }
        }
    }
}


