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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.codingapi.tx.annotation.TxTransaction;
import com.google.common.collect.Lists;
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.sys.SysParamConstants;
import com.prolog.eis.constants.wms.WmsConstants;
import com.prolog.eis.core.dto.rcs.RcsSendBindReqDto;
import com.prolog.eis.core.dto.route.AreaLocationCountDto;
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.outbound.*;
import com.prolog.eis.dto.item.ItemSyncVO;
import com.prolog.eis.dto.outbound.*;
import com.prolog.eis.dto.pms.PmsRobotTaskFinishReq;
import com.prolog.eis.dto.strategy.OrderDetailDto;
import com.prolog.eis.dto.strategy.OrderDto;
import com.prolog.eis.dto.wms.ItemContainerDto;
import com.prolog.eis.dto.wms.WmsContainerStatusUpdateDto;
import com.prolog.eis.enums.base.BaseStatusEnum;
import com.prolog.eis.enums.base.LayerEnum;
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.out.BizOutboundTaskDetailDismantle;
import com.prolog.eis.model.out.BizOutboundTaskDetailItem;
import com.prolog.eis.model.out.BizOutboundTaskDismantleSupply;
import com.prolog.eis.model.out.BizOutboundTaskDismantleSupplyHis;
import com.prolog.eis.model.outbound.*;
import com.prolog.eis.model.pick.PickingOrder;
import com.prolog.eis.model.shelves.PalletInfo;
import com.prolog.eis.model.shelves.PalletLocation;
import com.prolog.eis.outbound_dispatch.DispatchStrategy;
import com.prolog.eis.router.vo.ContainerLocationVo;
import com.prolog.eis.service.container.EmptyContainerTaskService;
import com.prolog.eis.service.container.LocationCfgService;
import com.prolog.eis.service.container.StationCfgService;
import com.prolog.eis.service.feign.FeignGoodsService;
import com.prolog.eis.service.feign.RouteFeignService;
import com.prolog.eis.service.inbound.BizInboundTaskService;
import com.prolog.eis.service.led.LedService;
import com.prolog.eis.service.outbound.BizOutboundTaskDetailItemHisService;
import com.prolog.eis.service.outbound.BizOutboundTaskDetailItemService;
import com.prolog.eis.service.outbound.BizOutboundTaskService;
import com.prolog.eis.service.pms.PmsService;
import com.prolog.eis.service.wms.WmsContainerStatusUpdateService;
import com.prolog.eis.service.wms.WmsOutBackService;
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.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.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
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;

/**
 * @author zmr
 * @description: 出库任务表-服务实现类
 * @date 2023-07-06 11:36
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class BizOutboundTaskServiceImpl implements BizOutboundTaskService {

    private final BizOutboundTaskMapper bizOutboundTaskMapper;
    private final BizOutboundTaskDetailMapper bizOutboundTaskDetailMapper;
    private final ContainerInfoDetailMapper containerInfoDetailMapper;
    private final ContainerInfoMapper containerInfoMapper;
    private final ContainerTaskMapper containerTaskMapper;
    private final RouteFeignService plgFeignRouteService;
    private final BizOutboundTaskHisMapper bizOutboundTaskHisMapper;
    private final BizOutboundTaskDetailHisMapper bizOutboundTaskDetailHisMapper;
    private final StationCfgService stationCfgService;
    private final ContainerStoreChangeMapper containerStoreChangeMapper;
    private final BizInboundTaskService bizInboundTaskService;
    private final PmsService pmsService;
    private final LocationCfgService locationCfgService;
    private final ContainerTaskHisMapper containerTaskHisMapper;
    private final WmsOutBackService wmsOutBackService;
    private final EmptyContainerTaskService emptyContainerTaskService;
    private final BizOutboundTaskDismantleSupplyMapper bizOutboundTaskDismantleSupplyMapper;
    private final BizOutboundTaskDetailDismantleMapper bizOutboundTaskDismantleMapper;
    private final BizOutboundTaskDismantleSupplyHisMapper bizOutboundTaskDismantleSupplyHisMapper;
    private final BizOutboundTaskDetailItemService bizOutboundTaskDetailItemService;
    private final RedisTemplate redisTemplate;
    private final LedService ledService;
    private final WmsContainerStatusUpdateService wmsContainerStatusUpdateService;
    private final PalletLocationMapper palletLocationMapper;
    private final FeignGoodsService feignGoodsService;
    private final PalletInfoMapper palletInfoMapper;
    private final OrderLogMapper orderLogMapper;
    private final BizOutboundTaskDetailItemService outboundTaskDetailItemService;
    private final BizOutboundTaskDetailItemHisService outboundTaskDetailItemHisService;


    @Value("${wms.warehouseId}")
    private String warehouseId;
    @Value("${wms.enterpriseId}")
    private String enterpriseId;

    /**
     * 新增数据
     *
     * @param bizOutboundTask 出库任务表
     */
    @Override
    public long save(BizOutboundTask bizOutboundTask) {
        return bizOutboundTaskMapper.save(bizOutboundTask);
    }

    /**
     * 新增批量数据
     *
     * @param bizOutboundTaskList 出库任务表
     */
    @Override
    public long saveBatch(List<BizOutboundTask> bizOutboundTaskList) {
        return bizOutboundTaskMapper.saveBatch(bizOutboundTaskList);
    }

    /**
     * 查询多条数据
     *
     * @param paramMap 查询起始位置
     * @return 对象列表
     */
    @Override
    public List<BizOutboundTask> findByMap(Map<String, Object> paramMap) {
        return bizOutboundTaskMapper.findByMap(paramMap, BizOutboundTask.class);
    }

    /**
     * 查询单条数据
     *
     * @param paramMap 查询起始位置
     * @return 对象
     */
    @Override
    public BizOutboundTask findOneByMap(Map<String, Object> paramMap) {
        List<BizOutboundTask> dataList = findByMap(paramMap);
        if (dataList.isEmpty()) {
            return null;
        }
        return dataList.get(0);
    }

    /**
     * 查询多条数据
     *
     * @param criteria 查询起始位置
     * @return 对象列表
     */
    @Override
    public List<BizOutboundTask> findByCriteria(Criteria criteria) {
        return bizOutboundTaskMapper.findByCriteria(criteria);
    }

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public BizOutboundTask findById(int id) {
        return bizOutboundTaskMapper.findById(id, BizOutboundTask.class);
    }

    /**
     * 修改数据
     *
     * @param bizOutboundTask 实例对象
     */
    @Override
    public long update(BizOutboundTask bizOutboundTask) {
        return bizOutboundTaskMapper.update(bizOutboundTask);
    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     */
    @Override
    public long deleteById(int id) {
        return bizOutboundTaskMapper.deleteById(id, BizOutboundTask.class);
    }

    /**
     * 通过参数删除数据
     *
     * @param paramMap 参数
     */
    @Override
    public long deleteByMap(Map<String, Object> paramMap) {
        return bizOutboundTaskMapper.deleteByMap(paramMap, BizOutboundTask.class);
    }

    /**
     * 通过参数查询总条数
     *
     * @param paramMap 参数
     */
    @Override
    public long findCountByMap(Map<String, Object> paramMap) {
        return bizOutboundTaskMapper.findCountByMap(paramMap, BizOutboundTask.class);
    }

    @Override
    public synchronized void emptyContainerOutBound(int num, String targetAreaNo, int flag) throws Exception {
        doEmptyContainerOutBound(num, targetAreaNo, flag);
    }

    @Transactional(rollbackFor = Exception.class)
    @TxTransaction(rollbackFor = Exception.class)
    public void doEmptyContainerOutBound(int num, String targetAreaNo, int flag) throws Exception {
        List<ContainerInfo> containerInfos = new ArrayList<>();

        List<ContainerInfo> containerInfoList = containerInfoMapper.findByMap(MapUtils.put("containerType", ContainerInfoTypeConstants.EMPTY_PALLET_PILE).put("taskType", ContainerTaskTypeConstants.IDLE).put("warehouseArea", WmsCarryTaskInCode.MJZ.getCode()).getMap(), ContainerInfo.class);
        if (!CollectionUtils.isEmpty(containerInfoList)) {
            containerInfos.addAll(containerInfoList);
        }
        if (CollectionUtils.isEmpty(containerInfos) || containerInfos.size() < num) {
            throw new PrologException("可出库空托数量不足");
        }
        if (!plgFeignRouteService.checkAreaIn(targetAreaNo)) {
            throw new PrologException("目标区域不可入");
        }
        List<String> containerNoList = containerInfos.stream().map(ContainerInfo::getContainerNo).collect(Collectors.toList());
        List<ContainerLocationVo> data = plgFeignRouteService.findLocationByContainerList(containerNoList);
        Map<String, Integer> collect = data.stream().collect(Collectors.toMap(ContainerLocationVo::getContainerNo, ContainerLocationVo::getDeptNum));
        for (ContainerInfo containerInfo : containerInfos) {
            containerInfo.setDeptNum(collect.getOrDefault(containerInfo.getContainerNo(), 99));
        }
        containerInfos = containerInfos.stream().sorted(Comparator.comparing(ContainerInfo::getDeptNum)).collect(Collectors.toList());
        List<CarryTask> carryTasks = new ArrayList<>();
        List<Integer> updateIdsList = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            ContainerInfo containerInfo = containerInfos.get(i);
            CarryTask carryTask = new CarryTask();
            carryTask.setId(StrUtil.uuid());
            carryTask.setPalletNo(containerInfo.getContainerNo());
            carryTask.setContainerNo(containerInfo.getContainerNo());
            carryTask.setTaskType(LocationConstants.PATH_TASK_TYPE_CARRY);
            carryTask.setEndRegion(targetAreaNo);
            carryTask.setCreateTime(new Date());
            carryTask.setBusinessType(String.valueOf(BaseTaskTypeConstants.EMPTY_OUT_TYPE));
            carryTask.setTaskRemark("空托搬运");
            carryTasks.add(carryTask);
            updateIdsList.add(containerInfo.getId());
        }
        Criteria criteria = new Criteria(ContainerInfo.class);
        criteria.restrictionBuilder().in(ContainerInfo::getId, updateIdsList).build();
        containerInfoMapper.updateMapByCriteria(MapUtils.put("taskType", ContainerTaskTypeConstants.OUTBOUND).getMap(), criteria);
        plgFeignRouteService.createBatchCarry(carryTasks);
    }

    @Override
    public long emptyContainerNum() {
        Criteria criteria = new Criteria(ContainerInfo.class);
        criteria.restrictionBuilder()
                .eq(ContainerInfo::getContainerType, ContainerInfoTypeConstants.EMPTY_PALLET_PILE)
                .eq(ContainerInfo::getTaskType, ContainerTaskTypeConstants.IDLE)
                .eq(ContainerInfo::getWarehouseArea, WmsCarryTaskInCode.MJZ.getCode())
                .build();
        long count = containerInfoMapper.findCountByCriteria(criteria);
        return count;
    }

    @Override
    public void specifyContainerCarry(SpecifyContainerCarryDto specifyContainerCarryDto) throws Exception {
        plgFeignRouteService.createCarryTask(specifyContainerCarryDto.getPalletNo(), LocationConstants.PATH_TASK_TYPE_CARRY, null, null, specifyContainerCarryDto.getTargetArea(), null,
                String.valueOf(BaseTaskTypeConstants.MOVE_TYPE), 0, "指定托盘搬运", null);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void wmsOutBound(List<OutBoundDto> outBoundDtos) throws Exception {
        groupOutBoundDto(outBoundDtos);
        for (OutBoundDto outBoundDto : outBoundDtos) {
            final List<OutBoundMainDto> subList = outBoundDto.getSubList();
            for (OutBoundMainDto outBoundMainDto : subList) {
                List<BizOutboundTask> bizOutboundTaskList = findByMap(null);
                BizOutboundTask bizOutboundTask = bizOutboundTaskList.stream().filter(o -> StrUtil.isNotBlank(o.getWaveNo()) && StrUtil.isNotBlank(outBoundMainDto.getWaveNo()) && o.getWaveNo().equals(outBoundMainDto.getWaveNo())
                        && o.getBillNo().equals(outBoundMainDto.getBillNo()) && o.getWmsLocationCode().equals(outBoundMainDto.getWmsLocationCode())).findFirst().orElse(null);
                if (!Objects.isNull(bizOutboundTask) && outBoundMainDto.getWmsLocationCode().equals(WmsConstants.AGVL)) {
                    //确定为拆零补货任务 查询当前订单品批缺货的数量  和下发的任务数量做比较
                    List<BizOutboundTaskDismantleSupply> supplyList = bizOutboundTaskDismantleSupplyMapper.findByMap(MapUtils.put("outboundId", bizOutboundTask.getId()).getMap(), BizOutboundTaskDismantleSupply.class);
                    Map<String, List<BizOutboundTaskDismantleSupply>> supplyMap = supplyList.stream().collect(Collectors.groupingBy(o -> o.getItemId() + "-" + o.getOwnerId() + "-" + o.getSupplyQty()));
                    for (OutBoundDetailDto outBoundDetailDto : outBoundMainDto.getSubList()) {
                        String key = outBoundDetailDto.getItemId() + "-" + outBoundDetailDto.getOwnerId() + "-" + outBoundDetailDto.getQty().setScale(2, BigDecimal.ROUND_HALF_UP);
                        if (!supplyMap.containsKey(key)) {
                            throw new Exception(String.format("订单号：【%s】物料：【%s】批次：【%s】,拆零库区下发补货任务数量和实际补货数量不符", outBoundMainDto.getBillNo(), outBoundDetailDto.getItemId(), outBoundDetailDto.getLotId()));
                        }
                        //如果存在相等补货数量
                        List<BizOutboundTaskDismantleSupply> tempList = supplyMap.get(key);
                        BizOutboundTaskDismantleSupply bizOutboundTaskDismantleSupply = tempList.get(0);
                        //转历史
                        tempList.remove(bizOutboundTaskDismantleSupply);
                        dismantleSupplyHis(bizOutboundTaskDismantleSupply);
                        //修改详情补货数量
                        BizOutboundTaskDetail bizOutboundTaskDetail = bizOutboundTaskDetailMapper.findById(bizOutboundTaskDismantleSupply.getOutboundDetailId(), BizOutboundTaskDetail.class);
                        AssertUtils.isNotTrue(bizOutboundTaskDismantleSupply.getSupplyQty().compareTo(bizOutboundTaskDetail.getSupplyQty()) > 0, String.format("订单详情ID:[%s],记录补货数量错误，请查看", bizOutboundTaskDetail.getId()));
                        bizOutboundTaskDetailMapper.updateMapById(bizOutboundTaskDismantleSupply.getOutboundDetailId(), MapUtils.put("supplyQty", bizOutboundTaskDetail.getSupplyQty().subtract(bizOutboundTaskDismantleSupply.getSupplyQty())).getMap(), BizOutboundTaskDetail.class);

                        //修改拆零详情补货数量
                        Criteria criteria = new Criteria(BizOutboundTaskDetailDismantle.class);
                        criteria.setRestriction(Restrictions.eq("outboundDetailId", bizOutboundTaskDetail.getId())
                                .and(Restrictions.eq("type", bizOutboundTaskDismantleSupply.getType())));
                        BizOutboundTaskDetailDismantle bizOutboundTaskDetailDismantle = bizOutboundTaskDismantleMapper.findByCriteria(criteria).stream().findFirst().orElse(null);
                        AssertUtils.isNotTrue(bizOutboundTaskDismantleSupply.getSupplyQty().compareTo(bizOutboundTaskDetailDismantle.getSupplyQty()) > 0, String.format("订单拆零详情ID:[%s],记录补货数量错误，请查看", bizOutboundTaskDetailDismantle.getId()));
                        bizOutboundTaskDismantleMapper.updateMapByCriteria(MapUtils.put("supplyQty", bizOutboundTaskDetailDismantle.getSupplyQty().subtract(bizOutboundTaskDismantleSupply.getSupplyQty())).getMap(), criteria);
                        //保存同订单补货详情任务
                        BizOutboundTaskDetail outboundTaskDetail = new BizOutboundTaskDetail();
                        outboundTaskDetail.setOutboundId(bizOutboundTask.getId());
                        outboundTaskDetail.setSeqNo(outBoundDetailDto.getSeqNo());
                        outboundTaskDetail.setOwnerId(outBoundDetailDto.getOwnerId());
                        outboundTaskDetail.setItemId(outBoundDetailDto.getItemId());
                        outboundTaskDetail.setLotId(outBoundDetailDto.getLotId());
                        outboundTaskDetail.setPackId(outBoundDetailDto.getPackId());
                        outboundTaskDetail.setContainerNo(outBoundDetailDto.getContainerNo());
                        outboundTaskDetail.setPlanQty(outBoundDetailDto.getQty());
                        outboundTaskDetail.setActQty(BigDecimal.ZERO);
                        outboundTaskDetail.setOutQty(BigDecimal.ZERO);
                        outboundTaskDetail.setLackQty(BigDecimal.ZERO);
                        outboundTaskDetail.setSupplyQty(BigDecimal.ZERO);
                        outboundTaskDetail.setLackState(-1);
                        outboundTaskDetail.setTargetArea(outBoundDetailDto.getTargetLocation());
                        outboundTaskDetail.setStatus(BaseStatusEnum.STATUS_INIT.getCode());
                        outboundTaskDetail.setCreateTime(LocalDateTime.now());
                        outboundTaskDetail.setCheckStore(0);
                        //补货任务标识
                        outboundTaskDetail.setIsSupply(1);
                        outboundTaskDetail.setUpdateTime(LocalDateTime.now());
                        outboundTaskDetail.setExtend1(outBoundDetailDto.getTaskId());
                        outboundTaskDetail.setExtend2(bizOutboundTaskDismantleSupply.getType().toString());
                        bizOutboundTaskDetailMapper.save(outboundTaskDetail);
                        redisTemplate.opsForHash().put("CL:SUPPER:" + bizOutboundTask.getId(), outboundTaskDetail.getId().toString(), "1");
                    }
                    continue;
                }
                if (StrUtil.equals(outBoundMainDto.getBusinessType(), BaseOutTypeConstants.INVENTORY_TYPE) || StrUtil.equals(outBoundMainDto.getBusinessType(), BaseOutTypeConstants.DO_ZDTP) || StrUtil.equals(outBoundMainDto.getBusinessType(), BaseOutTypeConstants.INV_CONSERVE)) {
                    if (outBoundMainDto.getWmsLocationCode().equals(WmsConstants.AGVL)) {
                        //订单出
                        saveBillOutTask(outBoundMainDto);
                    } else {
                        //容器出
                        saveTrayOutTask(outBoundMainDto);
                    }
                } else {
                    //订单出
                    saveBillOutTask(outBoundMainDto);
                }
            }
        }

    }

    private void dismantleSupplyHis(BizOutboundTaskDismantleSupply bizOutboundTaskDismantleSupply) {
        BizOutboundTaskDismantleSupplyHis bizOutboundTaskDetailHis = new BizOutboundTaskDismantleSupplyHis();
        BeanUtil.copyProperties(bizOutboundTaskDismantleSupply, bizOutboundTaskDetailHis);
        bizOutboundTaskDismantleSupplyMapper.deleteById(bizOutboundTaskDismantleSupply.getId(), BizOutboundTaskDismantleSupply.class);
        bizOutboundTaskDismantleSupplyHisMapper.save(bizOutboundTaskDetailHis);
    }


    /**
     * 保存容器出库订单
     *
     * @param outBoundDto
     */
    private void saveTrayOutTask(OutBoundMainDto outBoundDto) throws Exception {
        if (StringUtils.isBlank(outBoundDto.getContainerNo())) {
            throw new PrologException("容器级出库订单containerNo不能为空");
        }
        long count = containerInfoMapper.findCountByMap(MapUtils.put("containerNo", outBoundDto.getContainerNo()).getMap(), ContainerInfo.class);
        if (count <= 0) {
            throw new PrologException(String.format("容器【%s】库存不存在", outBoundDto.getContainerNo()));
        }
        Criteria criteria = Criteria.forClass(BizOutboundTaskDetail.class);
        criteria.restrictionBuilder().eq(BizOutboundTaskDetail::getContainerNo, outBoundDto.getContainerNo()).build();
        long count1 = bizOutboundTaskDetailMapper.findCountByCriteria(criteria);
        if (count1 > 0) {
            throw new PrologException(String.format("容器【%s】已下发出库任务", outBoundDto.getContainerNo()));
        }
        saveBillOutTask(outBoundDto);
    }

    /**
     * 保存单据出库
     *
     * @param outBoundDto
     */
    private void saveBillOutTask(OutBoundMainDto outBoundDto) throws Exception {
        Integer id = saveOutbound(outBoundDto);
        List<BizOutboundTaskDetail> outboundTaskDetailList = new ArrayList<>();
        List<BizOutboundTaskDetailItem> outboundItemList = new ArrayList<>();
        BizOutboundTaskDetailItem bizOutboundTaskDetailItem = null;
        BizOutboundTaskDetail outboundTaskDetail = null;
        boolean checkDetail = true;
        for (OutBoundDetailDto outBoundDetailDto : outBoundDto.getSubList()) {
            //是否为物料商品
            if (outBoundDetailDto.getIsMaterial() == 2) {
                bizOutboundTaskDetailItem = new BizOutboundTaskDetailItem();
                bizOutboundTaskDetailItem.setOutboundId(id);
                bizOutboundTaskDetailItem.setSeqNo(outBoundDetailDto.getSeqNo());
                bizOutboundTaskDetailItem.setPlanQty(outBoundDetailDto.getQty());
                bizOutboundTaskDetailItem.setCreateTime(new Date());
                bizOutboundTaskDetailItem.setUpdateTime(new Date());
                bizOutboundTaskDetailItem.setOwnerId(outBoundDetailDto.getOwnerId());
                bizOutboundTaskDetailItem.setItemId(outBoundDetailDto.getItemId());
                bizOutboundTaskDetailItem.setLotId(outBoundDetailDto.getLotId());
                bizOutboundTaskDetailItem.setPackId(outBoundDetailDto.getPackId());
                bizOutboundTaskDetailItem.setExtend1(outBoundDto.getBillNo());
                outboundItemList.add(bizOutboundTaskDetailItem);
                checkDetail = false;
                continue;
            }
            outboundTaskDetail = new BizOutboundTaskDetail();
            outboundTaskDetail.setOutboundId(id);
            outboundTaskDetail.setSeqNo(outBoundDetailDto.getSeqNo());
            outboundTaskDetail.setOwnerId(outBoundDetailDto.getOwnerId());
            outboundTaskDetail.setItemId(outBoundDetailDto.getItemId());
            outboundTaskDetail.setLotId(outBoundDetailDto.getLotId());
            outboundTaskDetail.setPackId(outBoundDetailDto.getPackId());
            outboundTaskDetail.setContainerNo(outBoundDetailDto.getContainerNo());
            outboundTaskDetail.setPlanQty(Objects.isNull(outBoundDetailDto.getQty()) ? BigDecimal.ZERO : outBoundDetailDto.getQty());
            outboundTaskDetail.setActQty(BigDecimal.ZERO);
            outboundTaskDetail.setOutQty(BigDecimal.ZERO);
            outboundTaskDetail.setLackQty(BigDecimal.ZERO);
            outboundTaskDetail.setSupplyQty(BigDecimal.ZERO);
            outboundTaskDetail.setLackState(-1);
            outboundTaskDetail.setTargetArea(outBoundDetailDto.getTargetLocation());
            outboundTaskDetail.setStatus(BaseStatusEnum.STATUS_INIT.getCode());
            outboundTaskDetail.setCreateTime(LocalDateTime.now());
            outboundTaskDetail.setUpdateTime(LocalDateTime.now());
            outboundTaskDetail.setCheckStore(0);
            outboundTaskDetail.setIsCheck(outBoundDetailDto.getIsCheck());
            //补货任务标识
            outboundTaskDetail.setIsSupply(0);
            outboundTaskDetail.setExtend1(outBoundDetailDto.getTaskId());
            outboundTaskDetailList.add(outboundTaskDetail);
        }
        if (CollectionUtils.isEmpty(outboundTaskDetailList) && checkDetail) {
            throw new PrologException(String.format("任务号[%s]没有出库明细", outBoundDto.getBillNo()));
        }
        if (!CollectionUtils.isEmpty(outboundItemList)) {
            bizOutboundTaskDetailItemService.saveBatch(outboundItemList);
        }
        if (!CollectionUtils.isEmpty(outboundTaskDetailList)) {
            bizOutboundTaskDetailMapper.saveBatch(outboundTaskDetailList);
        }
    }

    private Integer saveOutbound(OutBoundMainDto outBoundDto) throws Exception {
        BizOutboundTask outboundTask = new BizOutboundTask();
        outboundTask.setStatus(BaseStatusEnum.STATUS_INIT.getCode());
        outboundTask.setWorkType(outBoundDto.getWorkType());
        outboundTask.setCheckStore(0);
        outboundTask.setBillType(Integer.parseInt(outBoundDto.getWorkType()));
        outboundTask.setEnterpriseId(outBoundDto.getEnterpriseId());
        outboundTask.setBusinessType(outBoundDto.getBusinessType());
        outboundTask.setUpperSystemTaskId(outBoundDto.getTaskId());
        outboundTask.setWmsWarehouseId(outBoundDto.getWarehouseId());
        outboundTask.setBillNo(outBoundDto.getBillNo());
        outboundTask.setWaveNo(outBoundDto.getWaveNo());
        outboundTask.setPriority(outBoundDto.getPriority());
        outboundTask.setIsBindGrid(0);
        outboundTask.setCompanyId(outBoundDto.getCompanyId());
        outboundTask.setCompanyName(outboundTask.getCompanyName());
        outboundTask.setIsPrintInvoice(outBoundDto.getStr2());
        outboundTask.setIsPrintQuality(outBoundDto.getStr1());
        outboundTask.setWmsLocationCode(outBoundDto.getWmsLocationCode());
        outboundTask.setBillCount(1);
        outboundTask.setIsLock(0);
        outboundTask.setLackState(0);
        outboundTask.setScanFinish(0);
        outboundTask.setCreateTime(LocalDateTime.now());
        outboundTask.setUpdateTime(LocalDateTime.now());
        outboundTask.setExtend1(outBoundDto.getMsgId());
        outboundTask.setCheckType(outBoundDto.getCheckType());
        outboundTask.setProvince(outBoundDto.getProvince());
        outboundTask.setOrderCreateTime(Objects.isNull(outBoundDto.getOrderCreateTime()) ? LocalDateTime.now() : outBoundDto.getOrderCreateTime());
        bizOutboundTaskMapper.save(outboundTask);
        orderLogMapper.save(OrderLog.builder()
                .orderId(outboundTask.getId().toString())
                .orderNo(outBoundDto.getBillNo())
                .type("订单下发")
                .createTime(new Date())
                .build());
        return outboundTask.getId();
    }


    private void groupOutBoundDto(List<OutBoundDto> outBoundDtos) {
        for (OutBoundDto outBoundDto : outBoundDtos) {
            List<OutBoundMainDto> outBoundMainDtoList = new ArrayList<>();
            Map<String, List<OutBoundMainDto>> groupOutBound = outBoundDto.getSubList().stream().collect(Collectors.groupingBy(o -> o.getWaveNo() + "-" + o.getBillNo() + "-" + o.getWmsLocationCode()));
            groupOutBound.forEach((k, v) -> {
                OutBoundMainDto outBoundMainDto = v.get(0);
                List<OutBoundDetailDto> outBoundDetailDtoList = new ArrayList<>();
                v.forEach(item -> {
                    OutBoundDetailDto entity = new OutBoundDetailDto();
                    BeanUtils.copyProperties(item, entity);
                    entity.setSeqNo(item.getTaskLineId());
                    outBoundDetailDtoList.add(entity);
                });
                outBoundMainDto.setWarehouseId(outBoundDto.getWarehouseId());
                outBoundMainDto.setGroupOrderPolicy(outBoundDto.getGroupOrderPolicy());
                outBoundMainDto.setMsgId(outBoundDto.getMsgId());
                outBoundMainDto.setSubList(outBoundDetailDtoList);
                outBoundMainDtoList.add(outBoundMainDto);
            });
            outBoundDto.setSubList(outBoundMainDtoList);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TxTransaction(rollbackFor = Exception.class)
    public void taskFinishConfirm(List<ConfirmDto> confirmDtos) throws Exception {
        for (ConfirmDto confirmDto : confirmDtos) {
            Criteria criteria = Criteria.forClass(ContainerTask.class);
            criteria.restrictionBuilder().eq(ContainerTask::getContainerNo, confirmDto.getContainerNo()).build();
            criteria.orderDescBy(ContainerTask::getCreateTime);
            List<ContainerTask> containerTaskList = containerTaskMapper.findByCriteria(criteria);
            if (CollectionUtils.isEmpty(containerTaskList)) {
                throw new PrologException(String.format("没有托盘【%s】的出库任务", confirmDto.getContainerNo()));
            }

            List<ConfirmSublistDto> subList = confirmDto.getSubList();
            for (ConfirmSublistDto confirm : subList) {
                List<BizOutboundTask> outboundTaskList = bizOutboundTaskMapper.findByMap(MapUtils.put("billNo", confirm.getBillNo()).put("wmsLocationCode", confirm.getWmsLocationCode()).getMap(), BizOutboundTask.class);
                if (CollectionUtils.isEmpty(outboundTaskList)) {
                    throw new PrologException(String.format("没有单号【%s】的出库单", confirm.getTaskId()));
                }

                BizOutboundTaskDetail outboundTaskDetail = bizOutboundTaskDetailMapper.findByMap(MapUtils.put("seqNo", confirm.getTaskLineId()).getMap(), BizOutboundTaskDetail.class).stream().findFirst().orElse(null);
                if (Objects.isNull(outboundTaskDetail)) {
                    throw new PrologException(String.format("没有任务行号的【%s】的出库单详情", confirm.getTaskLineId()));
                }
                BizOutboundTask outboundTask = outboundTaskList.stream().filter(item -> Objects.equals(item.getId(), outboundTaskDetail.getOutboundId())).findFirst().orElseThrow(() -> new PrologException(String.format("根据行号%s和taskId%s没有找到对应订单", confirm.getTaskLineId(), confirm.getTaskId())));

                ContainerTask containerTask = containerTaskList.stream().filter(o -> o.getOutboundId().equals(outboundTask.getId()) && o.getOutboundDetailId().equals(outboundTaskDetail.getId())).findFirst().orElse(null);
                if (Objects.isNull(containerTask)) {
                    throw new PrologException(String.format("没有托盘【%s】,订单【%s】的出库任务", confirmDto.getContainerNo(), outboundTask.getId()));
                }
                outBoundConfirm(containerTask, confirm, outboundTask);
                toHis(outboundTask, outboundTaskDetail, containerTask);
            }
            this.sendBackLibrary(containerTaskList.get(0), subList.get(0));
        }
    }


    private void toHis(BizOutboundTask bizOutboundTask, BizOutboundTaskDetail outboundTaskDetail, ContainerTask containerTask) {
        ContainerTaskHis taskHis = new ContainerTaskHis();
        containerTask.setStatus(ContainerTaskStatusConstants.FINISH_STATUS);
        containerTask.setUpdateTime(LocalDateTime.now());
        BeanUtil.copyProperties(containerTask, taskHis);
        containerTaskHisMapper.save(taskHis);
        containerTaskMapper.deleteById(containerTask.getId(), ContainerTask.class);

        long count = containerTaskMapper.findCountByMap(MapUtils.put("outboundDetailId", containerTask.getOutboundDetailId()).getMap(), ContainerTask.class);
        if (count == 0) {
            //明细转历史
            outboundTaskDetail.setStatus(BaseStatusEnum.STATUS_FINISH.getCode());
            outboundTaskDetail.setUpdateTime(LocalDateTime.now());
            BizOutboundTaskDetailHis outTaskDetailHis = new BizOutboundTaskDetailHis();
            BeanUtils.copyProperties(outboundTaskDetail, outTaskDetailHis);
            bizOutboundTaskDetailHisMapper.save(outTaskDetailHis);
            //删除出库明细任务
            bizOutboundTaskDetailMapper.deleteById(containerTask.getOutboundDetailId(), BizOutboundTaskDetail.class);
        }
        //判断主任务是否完成
        count = bizOutboundTaskDetailMapper.findCountByMap(MapUtils.put("outboundId", containerTask.getOutboundId()).getMap(), BizOutboundTaskDetail.class);
        if (count == 0) {
            bizOutboundTask.setStatus(BaseStatusEnum.STATUS_FINISH.getCode());
            bizOutboundTask.setUpdateTime(LocalDateTime.now());
            BizOutboundTaskHis outTaskHis = new BizOutboundTaskHis();
            BeanUtils.copyProperties(bizOutboundTask, outTaskHis);
            bizOutboundTaskHisMapper.save(outTaskHis);
            //删除出库任务
            bizOutboundTaskMapper.deleteById(containerTask.getOutboundId(), BizOutboundTask.class);
        }
    }

    /**
     * 出库确认
     *
     * @return
     */
    private void outBoundConfirm(ContainerTask containerTask, ConfirmSublistDto confirmDto, BizOutboundTask outboundTask) {
        Criteria criteria = Criteria.forClass(ContainerInfoDetail.class);
        criteria.restrictionBuilder().eq(ContainerInfoDetail::getContainerNo, containerTask.getContainerNo())
                .eq(ContainerInfoDetail::getItemId, confirmDto.getItemId()).eq(ContainerInfoDetail::getLotId, confirmDto.getLotId()).build();
        ContainerInfoDetail containerInfoDetail = containerInfoDetailMapper.findByCriteria(criteria).stream().findFirst().orElseThrow(() -> new PrologException("没有查询到托盘【" + containerTask.getContainerNo() + "】的库存信息"));

        if (StrUtil.equals(confirmDto.getBusinessType(), BaseOutTypeConstants.INVENTORY_TYPE)) {
            //盘点出库 wms库存与实盘是否一致
            boolean flag = confirmDto.getQty().compareTo(confirmDto.getInventoryQty()) == 0;
            //盘点 wms库存与实盘一致 和我们库存数量不一致，修正数量
            if (flag && confirmDto.getQty().compareTo(containerInfoDetail.getQty()) != 0) {
                Map<String, Object> updateMap = MapUtils.put("qty", confirmDto.getQty()).getMap();
                if (confirmDto.getQty().compareTo(BigDecimal.ZERO) == 0) {
                    updateMap.put("packId", BasePropertyConstants.EMPTY_PALLET_PILE);
                    updateMap.put("ownerId", BasePropertyConstants.EMPTY_PALLET_PILE);
                    updateMap.put("lotId", BasePropertyConstants.EMPTY_PALLET_PILE);
                    updateMap.put("itemId", BasePropertyConstants.EMPTY_PALLET_PILE);
                    updateMap.put("isLock", 0);
                    updateMap.put("lockQty", 0);
                    updateMap.put("qty", 0);
                    Criteria containerInfoCriteria = new Criteria(ContainerInfo.class);
                    containerInfoCriteria.restrictionBuilder().eq(ContainerInfo::getContainerNo, containerInfoDetail.getContainerNo()).build();
                    containerInfoMapper.updateMapByCriteria(MapUtils.put("containerType", ContainerInfoTypeConstants.EMPTY_PALLET).getMap(), containerInfoCriteria);
                }
                saveOutReduceChange(Arrays.asList(containerInfoDetail), confirmDto.getWorkType(), confirmDto.getQty().subtract(containerInfoDetail.getQty()), confirmDto.getQty(), "盘点确认修正", outboundTask);
                containerInfoDetailMapper.updateMapById(containerInfoDetail.getId(), updateMap, ContainerInfoDetail.class);
            }
            if (confirmDto.lock()) {
                containerInfoDetailMapper.updateMapById(containerInfoDetail.getId(), MapUtils.put("lockQty", confirmDto.getQty()).put("isLock", 1).getMap(), ContainerInfoDetail.class);
            }
        } else if (StrUtil.equals(confirmDto.getBusinessType(), BaseOutTypeConstants.INV_CONSERVE)) {
            return;
        } else {
            //正常出库 扣减库存
            if (containerTask.getContainerType() != 0) {
                boolean compare = containerInfoDetail.getQty().compareTo(containerTask.getQty()) >= 0;
                Assert.isTrue(compare, "扣除托盘【{}】的库存异常，库存数量小于出库数量", containerTask.getContainerNo());
            }
//                BigDecimal remainQry =containerTask.getContainerType() != 0? containerInfoDetail.getQty().subtract(containerTask.getQty()):confirmDto.getInventoryQty();
            BigDecimal remainQry = containerInfoDetail.getQty().subtract(containerTask.getQty());
            BigDecimal lockQty = Objects.isNull(containerInfoDetail.getLockQty()) ? BigDecimal.ZERO : containerInfoDetail.getLockQty();

            saveOutReduceChange(Arrays.asList(containerInfoDetail), confirmDto.getWorkType(), containerTask.getContainerType() != 0 ? containerTask.getQty().negate() : confirmDto.getInventoryQty().subtract(containerInfoDetail.getQty()), remainQry, "拣选出库", outboundTask);

            if (remainQry.compareTo(BigDecimal.ZERO) == 0 && lockQty.compareTo(BigDecimal.ZERO) == 0) {
                Criteria containerInfoCriteria = new Criteria(ContainerInfo.class);
                containerInfoCriteria.restrictionBuilder().eq(ContainerInfo::getContainerNo, containerInfoDetail.getContainerNo()).build();
                containerInfoMapper.updateMapByCriteria(MapUtils.put("containerType", ContainerInfoTypeConstants.EMPTY_PALLET).getMap(), containerInfoCriteria);
                containerInfoDetailMapper.deleteById(containerInfoDetail.getId(), ContainerInfoDetail.class);
            } else {
                containerInfoDetailMapper.updateMapById(containerInfoDetail.getId(), MapUtils.put("qty", remainQry).getMap(), ContainerInfoDetail.class);
            }
        }
    }

    private void saveOutReduceChange(List<ContainerInfoDetail> containerInfoDetails, Integer workType, BigDecimal change, BigDecimal target, String remark, BizOutboundTask outboundTask) {
        List<ContainerStoreChange> storeChangeList = new ArrayList<>();
        ContainerStoreChange changeChange = null;
        for (ContainerInfoDetail containerInfoDetail : containerInfoDetails) {
            changeChange = new ContainerStoreChange();
            changeChange.setContainerNo(containerInfoDetail.getContainerNo());
            changeChange.setUpperSystemId(outboundTask.getBillNo());
            changeChange.setContainerSubNo(containerInfoDetail.getContainerSubNo());
            changeChange.setTaskType(workType);
            changeChange.setSoureQty(containerInfoDetail.getQty());
            changeChange.setChangeQty(change);
            changeChange.setTargetQty(target);
            changeChange.setRemark(remark);
            changeChange.setCreateTime(LocalDateTime.now());
            changeChange.setUpdateTime(LocalDateTime.now());
            changeChange.setPackId(containerInfoDetail.getPackId());
            changeChange.setOwnerId(containerInfoDetail.getOwnerId());
            changeChange.setLotId(containerInfoDetail.getLotId());
            changeChange.setItemId(containerInfoDetail.getItemId());
            storeChangeList.add(changeChange);
        }
        containerStoreChangeMapper.saveBatch(storeChangeList);
    }

    /**
     * 发送回库任务
     *
     * @throws Exception
     */
    private void sendBackLibrary(ContainerTask containerTask, ConfirmSublistDto confirmDto) throws Exception {
        LocationCfg locationCfg = locationCfgService.findByMap(MapUtils.put("wmsAreaNo", containerTask.getWmsLocationCode()).getMap()).stream().findFirst().orElseThrow(() -> new PrologException("回库点位未配置"));
        AssertUtils.isNotTrue(locationCfg == null, "库区未配置");

        String targetArea = locationCfg.getAreaNo();

        //四向库空托盘先回库  二楼单订单整托不用到叠盘机
        ContainerInfo containerInfo = containerInfoMapper.findByMap(MapUtils.put("containerNo", containerTask.getContainerNo()).getMap(), ContainerInfo.class).stream().findFirst().orElse(null);
        ContainerLocation containerLocation = plgFeignRouteService.seachLocationNo(MapUtils.put("containerNo", containerInfo.getContainerNo()).getMap());
        if (containerInfo.getContainerType() == ContainerInfoTypeConstants.EMPTY_PALLET) {
            //整托
            if (containerTask.getWmsLocationCode().equals(WmsConstants.MJZ)) {
                //判断当前托盘是否在整托站台
                Criteria criteria = new Criteria(StationCfg.class);
                criteria.setRestriction(Restrictions.in("stationType", new Integer[]{StationTypeEnum.SXKZT_TYPE.getCode(), StationTypeEnum.SXKJX_TYPE.getCode()}));
                List<StationCfg> stationCfgList = stationCfgService.findByCriteria(criteria);
                AssertUtils.isNotTrue(CollectionUtils.isEmpty(stationCfgList), "站台未配置");
                Map<String, Integer> stationCfgMap = stationCfgList.stream().collect(Collectors.toMap(StationCfg::getAreaNo, StationCfg::getStationType));
                AssertUtils.isNotTrue(!stationCfgMap.containsKey(containerLocation.getCurrentArea()), "站台配置和托盘当前区域不一致");
                //四向库整托清理点位
                if (stationCfgMap.get(containerLocation.getCurrentArea()).equals(StationTypeEnum.SXKZT_TYPE.getCode())) {
                    containerInfoDetailMapper.deleteByMap(MapUtils.put("containerNo", containerTask.getContainerNo()).getMap(), ContainerInfoDetail.class);
                    containerInfoMapper.deleteByMap(MapUtils.put("containerNo", containerTask.getContainerNo()).getMap(), ContainerInfo.class);
                    plgFeignRouteService.containerOffShelf(containerTask.getContainerNo());
                    return;
                }else{
                    LocationCfg stackCfg = locationCfgService.findOneByMap(MapUtils.put("type", LocationCfgConstants.DPJ).getMap());
                    AssertUtils.isNotTrue(Objects.isNull(stackCfg), "叠盘机点位未配置");
                    //优先入当前层
                    AssertUtils.isNotTrue(StrUtil.isEmpty(stackCfg.getWmsLocationNo()), "叠盘机所对应的楼层区域未配置");
                    AreaLocationCountDto locationCountDto = plgFeignRouteService.queryAreaLocationCountDtoList(Arrays.asList(stackCfg.getWmsLocationNo())).stream().findFirst().orElse(null);
                    if (locationCountDto.getFreeCount() <= 0) {
                        targetArea = bizInboundTaskService.findBestAreaByType(containerInfo.getContainerNo(), containerLocation.getCurrentArea(), containerTask.getItemId() + "@@" + containerTask.getLotId(), 1);
                    }else{
                        emptyContainerTaskService.save(containerInfo.getContainerNo(), LayerEnum.ONE, WmsCarryTaskInCode.MJZ);
                        return;
                    }
                }
            } else {
//                if (containerTask.getContainerType() == ContainerTaskStatusConstants.MORE_FULL_TRAY_TYPE || StrUtil.equals(confirmDto.getBusinessType(), BaseOutTypeConstants.INVENTORY_TYPE)) {
                if (!containerLocation.getCurrentArea().equals("C0103")) {
                    //发送去叠盘机任务
                    emptyContainerTaskService.save(containerInfo.getContainerNo(), LayerEnum.TWO, WmsCarryTaskInCode.MJZ);
                    return;
                } else {
                    containerInfoDetailMapper.deleteByMap(MapUtils.put("containerNo", containerTask.getContainerNo()).getMap(), ContainerInfoDetail.class);
                    containerInfoMapper.deleteByMap(MapUtils.put("containerNo", containerTask.getContainerNo()).getMap(), ContainerInfo.class);
                    plgFeignRouteService.containerOffShelf(containerTask.getContainerNo());
                    return;
                }
            }

        }
        if (containerInfo.getWarehouseArea().equals(WmsConstants.MJZ)) {
                targetArea = bizInboundTaskService.findBestAreaByType(containerInfo.getContainerNo(), containerLocation.getCurrentArea(), containerTask.getItemId() + "@@" + containerTask.getLotId(), 1);
        }
//        if (containerInfo.getWarehouseArea().equals(WmsConstants.MJZ)) {
//            if (containerInfo.getContainerType() == ContainerInfoTypeConstants.EMPTY_PALLET) {
//                LocationCfg stackCfg = locationCfgService.findOneByMap(MapUtils.put("type", LocationCfgConstants.DPJ).getMap());
//                AssertUtils.isNotTrue(Objects.isNull(stackCfg), "叠盘机点位未配置");
//                //优先入当前层
//                targetArea = stackCfg.getWmsLocationNo();
//                AssertUtils.isNotTrue(StrUtil.isEmpty(stackCfg.getWmsLocationNo()), "叠盘机所对应的楼层区域未配置");
//                AreaLocationCountDto locationCountDto = plgFeignRouteService.queryAreaLocationCountDtoList(Arrays.asList(stackCfg.getWmsLocationNo())).stream().findFirst().orElse(null);
//                if (locationCountDto.getFreeCount() <= 0) {
//                    targetArea = bizInboundTaskService.findBestAreaByType(containerInfo.getContainerNo(), containerLocation.getCurrentArea(), containerTask.getItemId() + "@@" + containerTask.getLotId(), 1);
//                }
//            } else {
//                targetArea = bizInboundTaskService.findBestAreaByType(containerInfo.getContainerNo(), containerLocation.getCurrentArea(), containerTask.getItemId() + "@@" + containerTask.getLotId(), 1);
//            }
//        }
        //有余料
        Criteria criteriaDetail = new Criteria(ContainerInfo.class);
        criteriaDetail.setRestriction(Restrictions.eq("containerNo", containerTask.getContainerNo()));
        containerInfoMapper.updateMapByCriteria(MapUtils.put("taskType", ContainerTaskTypeConstants.RETURN).put("updateTime", LocalDateTime.now()).getMap(), criteriaDetail);

        //非空托盘按品批给业务属性，修复回库业务属性BUG
        String businessProperty = null;
        if(containerInfo.getContainerType() != ContainerInfoTypeConstants.EMPTY_PALLET){
            businessProperty = containerTask.getItemId() + "@@" + containerTask.getLotId();
        }
        //创建搬运任务
        plgFeignRouteService.createCarryTask(containerInfo.getContainerNo(), null, null, null, targetArea, null,
                String.valueOf(BaseTaskTypeConstants.IN_TYPE), 1, "作业完成回库", businessProperty);
    }


    @Override
    public RestMessage updatePriority(List<PriorityDto> priorityList) {
        List<PriorityDto> errorList = Lists.newArrayList();
        for (PriorityDto priorityDto : priorityList) {
            Criteria criteria = new Criteria(BizOutboundTask.class);
            criteria.setRestriction(Restrictions.eq(BizOutboundTask::getBillNo, priorityDto.getBillNo()));
            BizOutboundTask outboundTask = bizOutboundTaskMapper.findByCriteria(criteria).stream().findFirst().orElse(null);
            if (Objects.isNull(outboundTask)) {
                priorityDto.setMsg("出库任务不存在");
                errorList.add(priorityDto);
                continue;
            }
            /*if (1 < outboundTask.getStatus()) {
                priorityDto.setMsg("当前出库任务状态不支持调整优先级");
                errorList.add(priorityDto);
                continue;
            }*/
            bizOutboundTaskMapper.updateMapByCriteria(MapUtils.put("priority", priorityDto.getPriority()).put("updateTime", new Date()).getMap(), criteria);
        }
        if (errorList.isEmpty()) {
            return RestMessage.newInstance(true, "请求成功");
        }
        return RestMessage.newInstance(false, "请求失败", errorList);
    }

    @Override
    public RestMessage inventoryAudit(List<InventoryAuditDto> inventoryAuditDtos) throws Exception {
        List<InventoryAuditDto> errorList = org.apache.commons.compress.utils.Lists.newArrayList();
        for (InventoryAuditDto goodsPdshVO : inventoryAuditDtos) {
            if (goodsPdshVO.getWorkType().equals("60")) {
                Criteria criteriaDetail = new Criteria(ContainerInfoDetail.class);
                criteriaDetail.setRestriction(Restrictions.eq("containerNo", goodsPdshVO.getContainerNo()).and(Restrictions.eq("lotId", goodsPdshVO.getLotId())).and(Restrictions.eq("itemId", goodsPdshVO.getItemId())));
                ContainerInfoDetail containerInfoDetail = containerInfoDetailMapper.findByCriteria(criteriaDetail).stream().findFirst().
                        orElseThrow(() -> new PrologException(String.format("容器【%s】的物料商品不存在无库存", goodsPdshVO.getContainerNo())));
                AssertUtils.isNotTrue(containerInfoDetail.getLockQty().compareTo(goodsPdshVO.getConfirmQty()) < 0, "解锁数量不能大于已锁定数量");
                BigDecimal lockQty = containerInfoDetail.getLockQty().subtract(goodsPdshVO.getConfirmQty());
                if (lockQty.compareTo(BigDecimal.ZERO) <= 0) {
                    lockQty = BigDecimal.ZERO;
                }
                containerInfoDetailMapper.updateMapById(containerInfoDetail.getId(), MapUtils.put("lockQty", lockQty).getMap(), ContainerInfoDetail.class);
            } else if (goodsPdshVO.getWorkType().equals("50")) {
                if (goodsPdshVO.getConfirmQty().compareTo(new BigDecimal(0)) < 0) {
                    goodsPdshVO.setMsg("盘点数量不能小于0");
                    errorList.add(goodsPdshVO);
                    return RestMessage.newInstance(false, "请求失败", errorList);
                }
                if (goodsPdshVO.getWmsLocationCode().equals(WmsConstants.AGVL)) {
                    //拆零库存
                    ContainerInfoDetail containerInfoDetail = containerInfoDetailMapper.findByMap(MapUtils.put("containerSubNo", goodsPdshVO.getLocNo()).getMap(), ContainerInfoDetail.class).stream().findFirst().orElse(null);
                    if (Objects.isNull(containerInfoDetail)) {
                        List<PalletLocation> palletLocations = palletLocationMapper.findByMap(MapUtils.put("palletLocation", goodsPdshVO.getLocNo()).getMap(), PalletLocation.class);
                        if (CollectionUtils.isEmpty(palletLocations)) {
                            throw new PrologException("没有找到货架信息");
                        }
                        List<ContainerInfo> containerInfos = containerInfoMapper.findByMap(MapUtils.put("containerNo", palletLocations.get(0).getPalletNo()).getMap(), ContainerInfo.class);

                        ItemSyncVO itemSyncVO = feignGoodsService.getItemInfo(goodsPdshVO.getItemId());

                        if (CollectionUtils.isEmpty(containerInfos)) {
                            ContainerInfo containerInfo = new ContainerInfo();
                            final List<PalletInfo> palletInfoList = palletInfoMapper.findByMap(MapUtils.put("palletNo", palletLocations.get(0).getPalletNo()).getMap(), PalletInfo.class);
                            org.springframework.util.Assert.notEmpty(palletInfoList, String.format("货架%s未维护！", palletLocations.get(0).getPalletNo()));
                            final List<PalletInfo> palletInfos = palletInfoList.stream().filter(x -> "0".equals(x.getPalletType()) || Objects.isNull(x.getPalletType())).collect(Collectors.toList());
                            if (!palletInfos.isEmpty()) {
                                Criteria criteria = Criteria.forClass(PalletInfo.class);
                                criteria.setRestriction(Restrictions.eq("palletNo", palletLocations.get(0).getPalletNo()));
                                palletInfoMapper.updateMapByCriteria(MapUtils.put("palletType", itemSyncVO.getItemHealthCategory()).put("updateTime", new Date()).getMap(), criteria);
                            }
                            containerInfo.setContainerType(ContainerInfoTypeConstants.GOODS_SHELF);
                            containerInfo.setContainerNo(palletLocations.get(0).getPalletNo());
                            containerInfo.setAreaType(AreaTypeConstant.AGV_CL);
                            containerInfo.setIsLock(0);
                            containerInfo.setTaskType(ContainerTaskTypeConstants.IDLE);
                            containerInfo.setCreateTime(LocalDateTime.now());
                            containerInfo.setWarehouseArea(WmsCarryTaskInCode.AGVL.getCode());
                            containerInfoMapper.save(containerInfo);
                        } else {
                            //校验同一面只能是同一个商品大类
                            List<PalletLocation> palletLocationList = palletLocationMapper.findByMap(MapUtils.put("palletNo", palletLocations.get(0).getPalletNo()).put("planes", palletLocations.get(0).getPlanes()).getMap(), PalletLocation.class);
                            if (!CollectionUtils.isEmpty(palletLocationList)) {
                                List<String> collect = palletLocationList.stream().map(PalletLocation::getPalletLocation).collect(Collectors.toList());
                                Criteria criteria = new Criteria(ContainerInfoDetail.class);
                                criteria.restrictionBuilder().in(ContainerInfoDetail::getContainerSubNo, collect).build();
                                List<ContainerInfoDetail> containerInfoDetailList = containerInfoDetailMapper.findByCriteria(criteria);
                                if (!CollectionUtils.isEmpty(containerInfoDetailList)) {
                                    List<String> itemIds = containerInfoDetailList.stream().map(ContainerInfoDetail::getItemId).collect(Collectors.toList());
                                    List<ItemSyncVO> itemSyncVOS = feignGoodsService.queryItemMst(itemIds);
                                    if (!StrUtil.equals(itemSyncVOS.get(0).getItemHealthCategory(), itemSyncVO.getItemHealthCategory())) {
                                        throw new PrologException("货架同一面商品大类必须一致");
                                    }
                                }

                            }

                        }

                        containerInfoDetail = new ContainerInfoDetail();
                        containerInfoDetail.setContainerNo(palletLocations.get(0).getPalletNo());
                        containerInfoDetail.setContainerSubNo(goodsPdshVO.getLocNo());
                        containerInfoDetail.setAreaType(AreaTypeConstant.AGV_CL);
                        containerInfoDetail.setItemId(goodsPdshVO.getItemId());
                        containerInfoDetail.setLotId(goodsPdshVO.getLotId());
                        containerInfoDetail.setOwnerId(goodsPdshVO.getOwnerId());
                        containerInfoDetail.setPackId(goodsPdshVO.getPackId());
                        containerInfoDetail.setIsLock(0);
                        containerInfoDetail.setQty(goodsPdshVO.getConfirmQty());
                        containerInfoDetail.setTaskType(0);
                        containerInfoDetail.setSingleVolume(itemSyncVO.getUomList().get(0).getVolume().divide(itemSyncVO.getUomList().get(0).getConversionRate(), 2, BigDecimal.ROUND_DOWN));
                        containerInfoDetail.setVolume(containerInfoDetail.getSingleVolume().multiply(containerInfoDetail.getQty()));
                        containerInfoDetail.setCreateTime(LocalDateTime.now());
                        containerInfoDetail.setLockQty(BigDecimal.ZERO);
                        containerInfoDetail.setMaxVolume(itemSyncVO.getPalletStdQuantity());
                        containerInfoDetailMapper.save(containerInfoDetail);

                        ContainerStoreChange changeChange = new ContainerStoreChange();
                        changeChange.setContainerNo(containerInfoDetail.getContainerNo());
                        changeChange.setContainerSubNo(containerInfoDetail.getContainerSubNo());
                        changeChange.setSoureQty(BigDecimal.ZERO);
                        changeChange.setChangeQty(containerInfoDetail.getQty());
                        changeChange.setTargetQty(containerInfoDetail.getQty());
                        changeChange.setRemark("移库新增");
                        changeChange.setCreateTime(LocalDateTime.now());
                        changeChange.setUpdateTime(LocalDateTime.now());
                        changeChange.setPackId(containerInfoDetail.getPackId());
                        changeChange.setOwnerId(containerInfoDetail.getOwnerId());
                        changeChange.setLotId(containerInfoDetail.getLotId());
                        changeChange.setItemId(containerInfoDetail.getItemId());
                        containerStoreChangeMapper.save(changeChange);
                    } else if (containerInfoDetail.getQty().compareTo(goodsPdshVO.getConfirmQty()) != 0) {
                        if (!StrUtil.equals(containerInfoDetail.getItemId(), goodsPdshVO.getItemId()) || !StrUtil.equals(containerInfoDetail.getLotId(), goodsPdshVO.getLotId())) {
                            throw new PrologException("货格物料必须同品同批");
                        }
                        if (!StrUtil.equals(containerInfoDetail.getContainerNo(), containerInfoDetail.getContainerSubNo())) {
                            //货架
                            if (goodsPdshVO.getConfirmQty().compareTo(BigDecimal.ZERO) == 0) {
                                containerInfoDetailMapper.deleteById(containerInfoDetail.getId(), ContainerInfoDetail.class);
                                List<ContainerInfoDetail> containerInfoDetailList = containerInfoDetailMapper.findByMap(MapUtils.put("containerNo", containerInfoDetail.getContainerNo()).getMap(), ContainerInfoDetail.class);
                                if (org.apache.commons.collections.CollectionUtils.isEmpty(containerInfoDetailList)) {
                                    containerInfoMapper.deleteByMap(MapUtils.put("containerNo", containerInfoDetail.getContainerNo()).getMap(), ContainerInfo.class);
                                }
                            } else {
                                containerInfoDetail.setLockQty(BigDecimal.ZERO);
                                containerInfoDetail.setIsLock(0);
                                containerInfoDetail.setQty(goodsPdshVO.getConfirmQty());
                                containerInfoDetail.setUpdateTime(LocalDateTime.now());
                                containerInfoDetail.setVolume(containerInfoDetail.getSingleVolume().multiply(containerInfoDetail.getQty()));
                                containerInfoDetailMapper.update(containerInfoDetail);
                            }
                        } else {
                            //托盘
                            containerInfoDetail.setQty(goodsPdshVO.getConfirmQty());
                            containerInfoDetail.setUpdateTime(LocalDateTime.now());
                            containerInfoDetail.setLockQty(BigDecimal.ZERO);
                            containerInfoDetail.setIsLock(0);
                            containerInfoDetailMapper.update(containerInfoDetail);
                            if (goodsPdshVO.getConfirmQty().compareTo(BigDecimal.ZERO) == 0) {
                                emptyContainerTaskService.save(containerInfoDetail.getContainerNo(), LayerEnum.TWO, WmsCarryTaskInCode.AGVL);
                                wmsContainerStatusUpdateService.execute(WmsContainerStatusUpdateDto.builder()
                                        .containerCode(containerInfoDetail.getContainerNo())
                                        .enterpriseId(enterpriseId)
                                        .warehouseId(warehouseId)
                                        .build());
                            }
                        }
                        ContainerStoreChange changeChange = new ContainerStoreChange();
                        changeChange.setContainerNo(containerInfoDetail.getContainerNo());
                        changeChange.setContainerSubNo(containerInfoDetail.getContainerSubNo());
                        changeChange.setSoureQty(containerInfoDetail.getQty());
                        changeChange.setChangeQty(goodsPdshVO.getConfirmQty().subtract(containerInfoDetail.getQty()));
                        changeChange.setTargetQty(goodsPdshVO.getConfirmQty());
                        changeChange.setRemark("盘点差异变更");
                        changeChange.setCreateTime(LocalDateTime.now());
                        changeChange.setUpdateTime(LocalDateTime.now());
                        changeChange.setPackId(containerInfoDetail.getPackId());
                        changeChange.setOwnerId(containerInfoDetail.getOwnerId());
                        changeChange.setLotId(containerInfoDetail.getLotId());
                        changeChange.setItemId(containerInfoDetail.getItemId());
                        containerStoreChangeMapper.save(changeChange);
                    } else {
                        containerInfoDetail.setLockQty(BigDecimal.ZERO);
                        containerInfoDetail.setIsLock(0);
                        containerInfoDetail.setUpdateTime(LocalDateTime.now());
                        containerInfoDetailMapper.update(containerInfoDetail);
                    }
                } else {
                    Criteria criteriaDetail = new Criteria(ContainerInfoDetail.class);
                    criteriaDetail.setRestriction(Restrictions.eq("containerNo", goodsPdshVO.getContainerNo()).and(Restrictions.eq("lotId", goodsPdshVO.getLotId())).and(Restrictions.eq("itemId", goodsPdshVO.getItemId())));
                    ContainerInfoDetail containerInfoDetail = containerInfoDetailMapper.findByCriteria(criteriaDetail).stream().findFirst().
                            orElseThrow(() -> new PrologException(String.format("容器【%s】的物料商品不存在无库存", goodsPdshVO.getContainerNo())));
                    if (goodsPdshVO.getConfirmQty().compareTo(containerInfoDetail.getQty()) != 0) {
                        Map<String, Object> updateMap = MapUtils.put("qty", goodsPdshVO.getConfirmQty()).getMap();
                        if (goodsPdshVO.getConfirmQty().compareTo(BigDecimal.ZERO) == 0) {
                            updateMap.put("packId", BasePropertyConstants.EMPTY_PALLET_PILE);
                            updateMap.put("ownerId", BasePropertyConstants.EMPTY_PALLET_PILE);
                            updateMap.put("lotId", BasePropertyConstants.EMPTY_PALLET_PILE);
                            updateMap.put("itemId", BasePropertyConstants.EMPTY_PALLET_PILE);
                            updateMap.put("isLock", 0);
                            updateMap.put("lockQty", 0);
                            Criteria criteria = new Criteria(ContainerInfo.class);
                            criteria.restrictionBuilder().eq(ContainerInfo::getContainerNo, containerInfoDetail.getContainerNo()).build();
                            containerInfoMapper.updateMapByCriteria(MapUtils.put("containerType", ContainerInfoTypeConstants.EMPTY_PALLET).getMap(), criteria);
                        }
                        containerInfoDetailMapper.updateMapById(containerInfoDetail.getId(), updateMap, ContainerInfoDetail.class);
                        //添加变更记录
                        ContainerStoreChange containerStoreChange = new ContainerStoreChange();
                        containerStoreChange.setContainerNo(goodsPdshVO.getContainerNo());
                        containerStoreChange.setContainerSubNo("");
                        containerStoreChange.setSoureQty(containerInfoDetail.getQty());
                        containerStoreChange.setChangeQty(goodsPdshVO.getConfirmQty().subtract(containerInfoDetail.getQty()));
                        containerStoreChange.setTargetQty(goodsPdshVO.getConfirmQty());
                        containerStoreChange.setUpperSystemId("");
                        containerStoreChange.setRemark("盘点差异变更");
                        containerStoreChange.setPackId(goodsPdshVO.getPackId());
                        containerStoreChange.setOwnerId(goodsPdshVO.getOwnerId());
                        containerStoreChange.setLotId(goodsPdshVO.getLotId());
                        containerStoreChange.setItemId(goodsPdshVO.getItemId());
                        containerStoreChange.setCreateTime(LocalDateTime.now());
                        containerStoreChange.setUpdateTime(LocalDateTime.now());
                        containerStoreChangeMapper.save(containerStoreChange);
                        containerInfoDetailMapper.updateMapById(containerInfoDetail.getId(), MapUtils.put("lockQty", 0).put("isLock", 0).getMap(), ContainerInfoDetail.class);
                    } else {
                        containerInfoDetailMapper.updateMapById(containerInfoDetail.getId(), MapUtils.put("lockQty", 0).put("isLock", 0).getMap(), ContainerInfoDetail.class);
                    }
                }
            } else if (goodsPdshVO.getWorkType().equals("20")) {
                //移库出库
                if (goodsPdshVO.getConfirmQty().compareTo(new BigDecimal(0)) < 0) {
                    goodsPdshVO.setMsg("数量不能小于0");
                    errorList.add(goodsPdshVO);
                    return RestMessage.newInstance(false, "请求失败", errorList);
                }
                Criteria criteriaDetail2 = new Criteria(ContainerInfoDetail.class);
                criteriaDetail2.setRestriction(Restrictions.eq("containerSubNo", goodsPdshVO.getLocNo()).and(Restrictions.eq("lotId", goodsPdshVO.getLotId())).and(Restrictions.eq("itemId", goodsPdshVO.getItemId())));
                ContainerInfoDetail containerInfoDetail = containerInfoDetailMapper.findByCriteria(criteriaDetail2).stream().findFirst().
                        orElseThrow(() -> new PrologException(String.format("容器【%s】的物料商品不存在无库存", goodsPdshVO.getContainerNo())));

                BigDecimal subQty = containerInfoDetail.getQty().subtract(goodsPdshVO.getConfirmQty());

                if (subQty.compareTo(BigDecimal.ZERO) == 0) {
                    Map<String, Object> updateMap = MapUtils.put("qty", subQty).put("lockQty", 0).getMap();
                    containerInfoDetailMapper.updateMapById(containerInfoDetail.getId(), updateMap, ContainerInfoDetail.class);
                    //  containerInfoDetailMapper.deleteById(containerInfoDetail.getId(), ContainerInfoDetail.class);
                    // List<ContainerInfoDetail> containerInfoDetailList = containerInfoDetailMapper.findByMap(MapUtils.put("containerNo", containerInfoDetail.getContainerNo()).getMap(), ContainerInfoDetail.class);
                    //  if (CollectionUtils.isEmpty(containerInfoDetailList)&&containerInfoDetail.getContainerNo().equals(containerInfoDetail.getContainerSubNo())) {
                    //      containerInfoMapper.deleteByMap(MapUtils.put("containerNo", containerInfoDetail.getContainerNo()).getMap(), ContainerInfo.class);
                    //}
                } else {
                    Map<String, Object> updateMap = MapUtils.put("qty", subQty).put("lockQty", 0).getMap();
                    if (!containerInfoDetail.getContainerNo().equals(containerInfoDetail.getContainerSubNo())) {
                        updateMap.put("volume", containerInfoDetail.getSingleVolume().multiply(subQty));
                    }
                    containerInfoDetailMapper.updateMapById(containerInfoDetail.getId(), updateMap, ContainerInfoDetail.class);
                }

                //添加变更记录
                ContainerStoreChange containerStoreChange = new ContainerStoreChange();
                containerStoreChange.setContainerNo(goodsPdshVO.getContainerNo());
                containerStoreChange.setContainerSubNo(goodsPdshVO.getLocNo());
                containerStoreChange.setSoureQty(containerInfoDetail.getQty());
                containerStoreChange.setChangeQty(goodsPdshVO.getConfirmQty().negate());
                containerStoreChange.setTargetQty(subQty);
                containerStoreChange.setUpperSystemId("");
                containerStoreChange.setRemark("移库差异变更");
                containerStoreChange.setPackId(goodsPdshVO.getPackId());
                containerStoreChange.setOwnerId(goodsPdshVO.getOwnerId());
                containerStoreChange.setLotId(goodsPdshVO.getLotId());
                containerStoreChange.setItemId(goodsPdshVO.getItemId());
                containerStoreChange.setCreateTime(LocalDateTime.now());
                containerStoreChange.setUpdateTime(LocalDateTime.now());
                containerStoreChangeMapper.save(containerStoreChange);

            } else {
                throw new PrologException(String.format("无法识别的wordType【%s】", goodsPdshVO.getWorkType()));
            }
        }
        return RestMessage.newInstance(true, "请求成功");
    }


    @Override
    public RestMessage inventoryLock(List<InventoryLockDto> inventoryLockDtos) {
        List<com.prolog.eis.dto.outbound.InventoryLockDto> errorList = org.apache.commons.compress.utils.Lists.newArrayList();
        for (InventoryLockDto inventoryLockDto : inventoryLockDtos) {
            Criteria criteriaDetail = new Criteria(ContainerInfoDetail.class);
            criteriaDetail.setRestriction(Restrictions.eq("containerNo", inventoryLockDto.getContainerNo()).and(Restrictions.eq("lotId", inventoryLockDto.getLotId())).and(Restrictions.eq("itemId", inventoryLockDto.getItemId())));
            ContainerInfoDetail containerInfoDetail = containerInfoDetailMapper.findByCriteria(criteriaDetail).stream().findFirst().
                    orElseThrow(() -> new PrologException(String.format("容器【%s】的物料商品不存在无库存", inventoryLockDto.getContainerNo())));
            BigDecimal confirmQty = inventoryLockDto.getConfirmQty();
            /*if (Objects.isNull(confirmQty)) {
                inventoryLockDto.setMsg("锁定数量不能为空");
                errorList.add(inventoryLockDto);
                return RestMessage.newInstance(false, "请求失败", errorList);
            }
            if (inventoryLockDto.getConfirmQty().compareTo(new BigDecimal(0)) < 0) {
                inventoryLockDto.setMsg("锁定数量不能小于0");
                errorList.add(inventoryLockDto);
                return RestMessage.newInstance(false, "请求失败", errorList);
            }
            BigDecimal lockQty = containerInfoDetail.getLockQty() == null ? new BigDecimal("0") : containerInfoDetail.getLockQty();
            */
            Map<String, Object> updateMap = MapUtils.put("updateTime", LocalDateTime.now()).getMap();
            if (Objects.isNull(confirmQty)) {
                updateMap.put("lockQty", containerInfoDetail.getQty());
            } else {
                BigDecimal lockQty = Objects.isNull(containerInfoDetail.getLockQty()) ? BigDecimal.ZERO : containerInfoDetail.getLockQty();
                /* BigDecimal lock = confirmQty.add(lockQty);*/
                updateMap.put("lockQty", lockQty.add(confirmQty));
            }
            if (containerInfoDetail.getLockQty().compareTo(containerInfoDetail.getQty()) > 0) {
                return RestMessage.newInstance(false, "请求失败:累计锁定数量不能大于库存数量", errorList);
            }

            containerInfoDetailMapper.updateMapById(containerInfoDetail.getId(), updateMap, ContainerInfoDetail.class);
        }
        return RestMessage.newInstance(true, "请求成功");
    }

    @Override
    public List<OrderDto> getOutboundTaskByBillType(String billType, String warehouseArea, DispatchStrategy dispatchStrategy) throws Exception {
        //eis组单调度开关
        String assemblyOrderSwitch = String.valueOf(redisTemplate.opsForValue().get(SysParamConstants.SYSPRE_FIX + SysParamConstants.ASSEMBLY_ORDER_SWITCH));
        List<OrderDto> orderDtoList = bizOutboundTaskMapper.getOutboundTaskByBillType(billType, warehouseArea, assemblyOrderSwitch);
        if (orderDtoList.isEmpty()) {
            return orderDtoList;
        }
        List<Integer> orderIds = orderDtoList.stream().map(OrderDto::getOrderId).collect(Collectors.toList());
        Map<Integer, List<OrderDetailDto>> map = getDetailByOrderIds(orderIds);
        for (OrderDto orderDto : orderDtoList) {
            List<OrderDetailDto> tempOrderDetailDtoList = map.get(orderDto.getOrderId());
            orderDto.setOrderDetailDtoList(tempOrderDetailDtoList);
        }
        return orderDtoList;
    }

    private Map<Integer, List<OrderDetailDto>> getDetailByOrderIds(List<Integer> orderIds) throws Exception {
        List<OrderDetailDto> orderDetailDtoList = bizOutboundTaskDetailMapper.getByOrderIds(orderIds);
        List<String> itemList = orderDetailDtoList.stream().map(OrderDetailDto::getItemId).distinct().collect(Collectors.toList());
        Map<String, ItemSyncVO> itemMap = feignGoodsService.queryItemMst(itemList).stream().collect(Collectors.toMap(ItemSyncVO::getItemId, o -> o));
        orderDetailDtoList.forEach(orderDetailDto -> {
            ItemSyncVO itemSyncVO = itemMap.get(orderDetailDto.getItemId());
            if (itemSyncVO != null) {
                orderDetailDto.setAbcType(itemSyncVO.getAbcType());
            }
        });
        return orderDetailDtoList.stream().collect(Collectors.groupingBy(OrderDetailDto::getOrderId));
    }

    @Override
    public void batchUpdate(List<BizOutboundTask> updateBizOutboundTaskList) {
        for (BizOutboundTask bizOutboundTask : updateBizOutboundTaskList) {
            update(bizOutboundTask);
        }
    }


    @Override
    @Transactional
    public RestMessage taskCancel(List<CancelDto> cancelList) {
        List<CancelDto> errorList = org.apache.commons.compress.utils.Lists.newArrayList();
        for (CancelDto cancelDto : cancelList) {
            if (!StringUtils.isBlank(cancelDto.getTaskLineId())) {
                BizOutboundTaskDetail bizOutboundTaskDetail = bizOutboundTaskDetailMapper.findByMap(MapUtils.put("seqNo", cancelDto.getTaskLineId()).getMap(), BizOutboundTaskDetail.class).stream().findFirst().orElse(null);
                if (bizOutboundTaskDetail == null || bizOutboundTaskDetail.getStatus() > 0) {
                    cancelDto.setMsg("当前出库任务不可取消");
                    errorList.add(cancelDto);
                    continue;
                }
                outTaskToHis(bizOutboundTaskDetail);
            } else if (!StringUtils.isBlank(cancelDto.getContainerNo())) {
                BizOutboundTaskDetail bizOutboundTaskDetail = bizOutboundTaskDetailMapper.findByMap(MapUtils.put("containerNo", cancelDto.getContainerNo()).getMap(), BizOutboundTaskDetail.class).stream().findFirst().orElse(null);
                if (bizOutboundTaskDetail == null || bizOutboundTaskDetail.getStatus() > 0) {
                    cancelDto.setMsg("当前出库任务不可取消");
                    errorList.add(cancelDto);
                    continue;
                }
                outTaskToHis(bizOutboundTaskDetail);
            } else {
                Criteria criteria = new Criteria(BizOutboundTask.class);
                criteria.setRestriction(Restrictions.eq(BizOutboundTask::getBillNo, cancelDto.getBillNo()));
                BizOutboundTask outboundTask = bizOutboundTaskMapper.findByCriteria(criteria).stream().findFirst().orElse(null);
                if (Objects.isNull(outboundTask) || outboundTask.getStatus() > 0) {
                    cancelDto.setMsg("当前出库任务不可取消");
                    errorList.add(cancelDto);
                    continue;
                }
                BizOutboundTaskHis his = new BizOutboundTaskHis();
                BeanUtils.copyProperties(outboundTask, his);
                his.setStatus(-10);
                his.setUpdateTime(LocalDateTime.now());
                bizOutboundTaskMapper.deleteById(outboundTask.getId(), BizOutboundTask.class);
                bizOutboundTaskHisMapper.save(his);

                List<BizOutboundTaskDetail> detailList = bizOutboundTaskDetailMapper.findByMap(MapUtils.put("outboundId", outboundTask.getId()).getMap(), BizOutboundTaskDetail.class);
                List<BizOutboundTaskDetailHis> detailHisList = new ArrayList<>();
                BizOutboundTaskDetailHis detailHis = null;
                for (BizOutboundTaskDetail detail : detailList) {
                    detailHis = new BizOutboundTaskDetailHis();
                    BeanUtils.copyProperties(detail, detailHis);
                    detailHis.setStatus(-10);
                    detailHis.setUpdateTime(LocalDateTime.now());
                    detailHisList.add(detailHis);
                }
                bizOutboundTaskDetailMapper.deleteByMap(MapUtils.put("outboundId", outboundTask.getId()).getMap(), BizOutboundTaskDetail.class);
                bizOutboundTaskDetailHisMapper.saveBatch(detailHisList);
            }
        }
        if (errorList.isEmpty()) {
            return RestMessage.newInstance(true, "请求成功");
        }
        return RestMessage.newInstance(false, "请求失败", errorList);
    }


    private void outTaskToHis(BizOutboundTaskDetail bizOutboundTaskDetail) {
        BizOutboundTaskDetailHis detailHis = new BizOutboundTaskDetailHis();
        BeanUtils.copyProperties(bizOutboundTaskDetail, detailHis);
        detailHis.setStatus(-10);
        detailHis.setUpdateTime(LocalDateTime.now());
        bizOutboundTaskDetailMapper.deleteById(bizOutboundTaskDetail.getId(), BizOutboundTaskDetail.class);
        bizOutboundTaskDetailHisMapper.save(detailHis);

        long count = bizOutboundTaskDetailMapper.findCountByMap(MapUtils.put("outboundId", bizOutboundTaskDetail.getOutboundId()).getMap(), BizOutboundTaskDetail.class);
        if (count == 0) {
            BizOutboundTask out = bizOutboundTaskMapper.findById(bizOutboundTaskDetail.getOutboundId(), BizOutboundTask.class);
            BizOutboundTaskHis his = new BizOutboundTaskHis();
            BeanUtils.copyProperties(out, his);
            his.setStatus(-10);
            his.setUpdateTime(LocalDateTime.now());
            bizOutboundTaskMapper.deleteById(bizOutboundTaskDetail.getOutboundId(), BizOutboundTask.class);
            bizOutboundTaskHisMapper.save(his);
        }
    }


    @Override
    public List<ContainerLocation> freeContainer(FreeContainerDto freeContainerDto) throws Exception {
        return plgFeignRouteService.findFreeContainerByAreaNo(freeContainerDto.getAreaNos());
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void robotTaskFinish(PmsRobotTaskFinishReq pmsRobotTaskFinishReq) throws Exception {
        //根据容器查容器任务
        List<ContainerTask> containerTaskList = containerTaskMapper.findByMap(MapUtils.put("containerNo", pmsRobotTaskFinishReq.getContainerNo()).getMap(), ContainerTask.class);
        AssertUtils.isNotTrue(CollectionUtils.isEmpty(containerTaskList), String.format("托盘：[%s]无该托盘的容器任务", pmsRobotTaskFinishReq.getContainerNo()));

        outboundConfirm(containerTaskList, pmsRobotTaskFinishReq);

    }


    public void outboundConfirm(List<ContainerTask> containerTaskList, PmsRobotTaskFinishReq
            pmsRobotTaskFinishReq) throws Exception {
        ContainerInfoDetail containerInfoDetail = containerInfoDetailMapper.findByMap(MapUtils.put("containerNo", pmsRobotTaskFinishReq.getContainerNo()).getMap(), ContainerInfoDetail.class).stream().findFirst().orElse(null);
        AssertUtils.isNotTrue(containerInfoDetail == null, String.format("容器商品明细不存在，容器号：[%s]", pmsRobotTaskFinishReq.getContainerNo()));

        ContainerInfo containerInfo = containerInfoMapper.findByMap(MapUtils.put("containerNo", pmsRobotTaskFinishReq.getContainerNo()).getMap(), ContainerInfo.class).stream().findFirst().orElse(null);
        AssertUtils.isNotTrue(containerInfo == null, String.format("容器商品不存在，容器号：[%s]", pmsRobotTaskFinishReq.getContainerNo()));


        ContainerTask container = containerTaskList.get(0);

        //正常出库 扣减库存
        boolean compare = containerInfoDetail.getQty().compareTo(pmsRobotTaskFinishReq.getQty()) >= 0;
        Assert.isTrue(compare, "扣除托盘【{}】的库存异常，库存数量小于拣选数量", pmsRobotTaskFinishReq.getContainerNo());
        BigDecimal remainQry = containerInfoDetail.getQty().subtract(pmsRobotTaskFinishReq.getQty());

        ContainerStoreChange changeChange = null;
        changeChange = new ContainerStoreChange();
        changeChange.setContainerNo(containerInfoDetail.getContainerNo());
        changeChange.setContainerSubNo(containerInfoDetail.getContainerSubNo());
        changeChange.setTaskType(container.getTaskType());
        changeChange.setSoureQty(containerInfoDetail.getQty());
        changeChange.setChangeQty(pmsRobotTaskFinishReq.getQty().negate());
        changeChange.setTargetQty(remainQry);
        changeChange.setRemark(container.getTaskType() == 10 ? "产成品出库" : "砂芯出库");
        changeChange.setCreateTime(LocalDateTime.now());
        changeChange.setUpdateTime(LocalDateTime.now());
        changeChange.setPackId(containerInfoDetail.getPackId());
        changeChange.setOwnerId(containerInfoDetail.getOwnerId());
        changeChange.setLotId(containerInfoDetail.getLotId());
        changeChange.setItemId(containerInfoDetail.getItemId());
        containerStoreChangeMapper.save(changeChange);

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("taskType", ContainerTaskTypeConstants.RETURN);
        dataMap.put("updateTime", new Date());
        if (remainQry.compareTo(BigDecimal.ZERO) == 0) {
            containerInfoDetail.setQty(new BigDecimal(1));
            containerInfoDetail.setItemId(BasePropertyConstants.EMPTY_PALLET_PILE);
            containerInfoDetail.setLotId(BasePropertyConstants.EMPTY_PALLET_PILE);
            containerInfoDetail.setOwnerId(BasePropertyConstants.EMPTY_PALLET_PILE);
            containerInfoDetail.setPackId(BasePropertyConstants.EMPTY_PALLET_PILE);

            dataMap.put("containerType", 1);
        } else {
            containerInfoDetail.setQty(remainQry);
        }
        containerInfoMapper.updateMapById(containerInfo.getId(), dataMap, ContainerInfo.class);
        containerInfoDetailMapper.update(containerInfoDetail);
        //修改缓存数量
        StationCfg stationCfg = stationCfgService.findOneByMap(MapUtils.put("areaNo", container.getExtend2()).getMap());
        AssertUtils.isNotTrue(stationCfg == null, String.format("拣选完成找不到站台数据：[%s]", container.getExtend2()));
        String crrContainerNo = Arrays.asList(stationCfg.getCurrentContainerNo().split(",")).stream().filter(v -> !v.equals(container.getContainerNo())).collect(Collectors.joining());
        //减少缓存数
        stationCfgService.updateMapById(stationCfg.getId(), MapUtils.put("currentCacheNum", stationCfg.getCurrentCacheNum() - 1).put("updateTime", new Date()).put("currentContainerNo", crrContainerNo).getMap());
        //发送回库任务
        LocationCfg locationCfg = locationCfgService.findOneByMap(MapUtils.put("type", 30).getMap());
        AssertUtils.isNotTrue(locationCfg == null, String.format("回库点位未配置：拣选完成托盘[%s]", container.getContainerNo()));

        List<ContainerTaskHis> hisList = new ArrayList<>();
        for (ContainerTask containerTask : containerTaskList) {
            containerTask.setStatus(ContainerTaskStatusConstants.FINISH_STATUS);
            containerTask.setFinishTime(LocalDateTime.now());
            ContainerTaskHis taskHis = new ContainerTaskHis();
            BeanUtils.copyProperties(containerTask, taskHis);
            hisList.add(taskHis);
        }
        containerTaskHisMapper.saveBatch(hisList);
        containerTaskMapper.deleteByMap(MapUtils.put("containerNo", pmsRobotTaskFinishReq.getContainerNo()).getMap(), ContainerTask.class);

        //回告wms
        List<WmsOutCbReqDto> wmsOutCbReqDtos = setWmsOutBackReqDto(containerTaskList);

        //拣选单、出库单、出库单详情转历史
        doOutTaskToHis(containerTaskList);
        //发送回库任务
        pmsService.carryTask(container.getContainerNo(), stationCfg.getAreaNo(), locationCfg.getAreaNo());

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

    @Override
    public List<WmsOutCbReqDto> setWmsOutBackReqDto(List<ContainerTask> containerTaskList) {
        WmsOutCbReqDto call = null;
        List<WmsOutDetailCbReqDto> wmsOutBackDetailReqDtoList = new ArrayList<>();
        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(bizOutboundTaskDetail)) {
                throw new PrologException(String.format("通过主键Id【%s】未查询到出库任务明细", containerTask.getOutboundDetailId()));
            }
            WmsOutDetailCbReqDto WmsOutBackDetailReqDto = setWmsOutBackDetailReqDto(bizOutboundTask, bizOutboundTaskDetail, 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.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;
    }

    /**
     * 出库任务+出库明细转历史
     *
     * @param containerTaskList
     */
    private void doOutTaskToHis(List<ContainerTask> containerTaskList) throws Exception {
        BizOutboundTaskDetail outTaskDetail = null;
        BizOutboundTask outTask = null;
        PickingOrder pickingOrder = null;
        for (ContainerTask containerTask : containerTaskList) {
            //判断任务明细是否完成
            long count = containerTaskMapper.findCountByMap(MapUtils.put("outboundDetailId", containerTask.getOutboundDetailId()).getMap(), ContainerTask.class);
            if (count == 0) {
                //明细转历史
                outTaskDetail = bizOutboundTaskDetailMapper.findById(containerTask.getOutboundDetailId(), BizOutboundTaskDetail.class);
                if (outTaskDetail == null) {
                    throw new Exception("出库任务明细不存在，任务ID：" + containerTask.getOutboundDetailId());
                }
                outTaskDetail.setStatus(BaseStatusEnum.STATUS_FINISH.getCode());
                outTaskDetail.setUpdateTime(LocalDateTime.now());
                //删除出库明细任务
                bizOutboundTaskDetailMapper.deleteById(containerTask.getOutboundDetailId(), BizOutboundTaskDetail.class);
                //出库明细任务转历史
                BizOutboundTaskDetailHis outTaskDetailHis = new BizOutboundTaskDetailHis();
                BeanUtils.copyProperties(outTaskDetail, outTaskDetailHis);
                bizOutboundTaskDetailHisMapper.save(outTaskDetailHis);
            }
            //判断主任务是否完成
            count = bizOutboundTaskDetailMapper.findCountByMap(MapUtils.put("outboundId", containerTask.getOutboundId()).getMap(), BizOutboundTaskDetail.class);
            if (count == 0) {
                //任务转历史
                outTask = bizOutboundTaskMapper.findById(containerTask.getOutboundId(), BizOutboundTask.class);
                if (outTask == null) {
                    throw new Exception("出库任务不存在，任务ID：" + containerTask.getOutboundId());
                }
                outTask.setStatus(BaseStatusEnum.STATUS_FINISH.getCode());
                outTask.setUpdateTime(LocalDateTime.now());
                //删除出库任务
                bizOutboundTaskMapper.deleteById(containerTask.getOutboundId(), BizOutboundTask.class);
                //出库任务转历史
                BizOutboundTaskHis outTaskHis = new BizOutboundTaskHis();
                BeanUtils.copyProperties(outTask, outTaskHis);
                bizOutboundTaskHisMapper.save(outTaskHis);
            }
        }
    }

    @Override
    public void sendEmptyToCache() throws Exception {
        LocationCfg locationCfg = locationCfgService.findByMap(MapUtils.put("type", LocationCfgConstants.SJKT).getMap()).stream().findFirst().orElseThrow(() -> new PrologException("一楼空托垛补给位未配置"));
        emptyContainerOutBound(1, locationCfg.getAreaNo(), 0);
    }

    @Override
    public void moveEmptyContainer() throws Exception {
        //1 查询上架区域已绑定的空托
        LocationCfg locationCfg = locationCfgService.findByMap(MapUtils.put("type", LocationCfgConstants.SJW).getMap()).stream().findFirst().orElseThrow(() -> new PrologException("上架商品位区域未配置"));
        List<ContainerLocation> freeContainerList = plgFeignRouteService.findFreeContainerByAreaNo(locationCfg.getAreaNo()).stream().filter(item -> Objects.equals(item.getBusinessProperty(), BasePropertyConstants.SJ_EMPTY)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(freeContainerList)) {
            return;
        }
        List<String> freeContainerNo = freeContainerList.stream().map(ContainerLocation::getContainerNo).collect(Collectors.toList());
        List<String> carryTaskContainer = plgFeignRouteService.findCarryTaskListByContainerList(freeContainerNo).stream().map(CarryTask::getContainerNo).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(carryTaskContainer)) {
            freeContainerList = freeContainerList.stream().filter(item -> !carryTaskContainer.contains(item.getContainerNo())).collect(Collectors.toList());
        }
        if (CollectionUtils.isEmpty(freeContainerList)) {
            return;
        }
        //2 查询目标位置
        LocationCfg targetLocation = locationCfgService.findByMap(MapUtils.put("type", LocationCfgConstants.JXKT).getMap()).stream().findFirst().orElseThrow(() -> new PrologException("拣选订单空托补给区域未配置"));

        List<CarryTask> carryTaskList = new ArrayList<>();
        for (ContainerLocation freeContainer : freeContainerList) {
            //3 判断是否有空闲货位
            if (plgFeignRouteService.checkAreaIn(targetLocation.getAreaNo())) {
                carryTaskList.add(plgFeignRouteService.setCarryTask(freeContainer.getContainerNo(), null, freeContainer.getCurrentArea(), freeContainer.getCurrentLocation(), targetLocation.getAreaNo(), null, String.valueOf(BaseTaskTypeConstants.MOVE_TYPE), 1, "空托移库", null, 99));
            }

        }
        //5 搬运
        if (!carryTaskList.isEmpty()) {
            plgFeignRouteService.createBatchCarry(carryTaskList);
        }
    }

    @Override
    public List<ItemContainerDto> itemContainer(ItemContainerDto req) throws Exception {
        List<ItemContainerDto> list = containerInfoMapper.itemContainer(req);
        if (CollectionUtils.isEmpty(list)) {
            List<PalletLocation> palletLocations = palletLocationMapper.findByMap(MapUtils.put("palletLocation", req.getContainerNo()).getMap(), PalletLocation.class);
            if (CollectionUtils.isEmpty(palletLocations)) {
                List<ContainerLocationVo> locationByContainerList = plgFeignRouteService.findLocationByContainerListNoCheck(Arrays.asList(req.getContainerNo()));
                if (CollectionUtils.isEmpty(locationByContainerList)) {
                    throw new PrologException("查询到的库存为空");
                } else {
                    return list;
                }
            } else {
                List<ContainerLocationVo> locationByContainerList = plgFeignRouteService.findLocationByContainerListNoCheck(Arrays.asList(palletLocations.get(0).getPalletNo()));
                if (CollectionUtils.isEmpty(locationByContainerList)) {
                    throw new PrologException("查询到的库存为空");
                } else {
                    return list;
                }
            }
        }
        AssertUtils.isNotTrue(CollectionUtils.isEmpty(list), "查询到的库存为空");
        List<String> containerNoList = list.stream().map(ItemContainerDto::getContainer).collect(Collectors.toList());
        final List<ContainerLocationVo> locationByContainerList = plgFeignRouteService.findLocationByContainerList(containerNoList);
        for (ItemContainerDto itemContainerDto : list) {
            final ContainerLocationVo containerLocationVo = locationByContainerList.stream().filter(item -> item.getContainerNo().equals(itemContainerDto.getContainer())).findFirst().get();
            itemContainerDto.setCurLocation(containerLocationVo.getCurrentLocation());
            itemContainerDto.setCurLayer(String.valueOf(containerLocationVo.getLayer()));
        }
        return list;
    }

    @Override
    public void sendLedMsg(RcsSendBindReqDto rcsSendBindReqDto) {
    }
}
