package com.huaheng.api.mes.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.huaheng.api.mes.domain.*;
import com.huaheng.common.constant.HttpConstant;
import com.huaheng.common.constant.QuantityConstant;
import com.huaheng.common.exception.service.ServiceException;
import com.huaheng.common.support.Convert;
import com.huaheng.common.utils.DateUtils;
import com.huaheng.common.utils.StringUtils;
import com.huaheng.common.utils.bean.BeanUtils;
import com.huaheng.common.utils.restful.RestUtil;
import com.huaheng.common.utils.security.ShiroUtils;
import com.huaheng.framework.web.domain.AjaxResult;
import com.huaheng.pc.config.address.service.AddressService;
import com.huaheng.pc.config.container.domain.Container;
import com.huaheng.pc.config.container.service.ContainerService;
import com.huaheng.pc.config.location.domain.Location;
import com.huaheng.pc.config.location.service.LocationService;
import com.huaheng.pc.config.material.domain.Material;
import com.huaheng.pc.config.material.service.MaterialService;
import com.huaheng.pc.config.materialType.domain.MaterialType;
import com.huaheng.pc.config.materialType.service.MaterialTypeService;
import com.huaheng.pc.config.supplier.domain.Supplier;
import com.huaheng.pc.config.supplier.service.SupplierService;
import com.huaheng.pc.config.vehicle.domain.Vehicle;
import com.huaheng.pc.config.vehicle.service.VehicleService;
import com.huaheng.pc.config.vehicleType.domain.VehicleType;
import com.huaheng.pc.config.vehicleType.service.VehicleTypeService;
import com.huaheng.pc.config.vehicleTypeCoordinate.service.VehicleTypeCoordinateService;
import com.huaheng.pc.config.zone.domain.Zone;
import com.huaheng.pc.config.zone.service.ZoneService;
import com.huaheng.pc.inventory.inventoryDetail.domain.InventoryDetail;
import com.huaheng.pc.inventory.inventoryDetail.service.InventoryDetailService;
import com.huaheng.pc.inventory.inventoryHeader.domain.InventoryHeader;
import com.huaheng.pc.inventory.inventoryHeader.service.InventoryHeaderService;
import com.huaheng.pc.receipt.receiptContainerDetail.domain.ReceiptContainerDetail;
import com.huaheng.pc.receipt.receiptContainerDetail.service.ReceiptContainerDetailService;
import com.huaheng.pc.receipt.receiptContainerHeader.domain.ReceiptContainerHeader;
import com.huaheng.pc.receipt.receiptContainerHeader.service.ReceiptContainerHeaderService;
import com.huaheng.pc.receipt.receiptDetail.domain.ReceiptDetail;
import com.huaheng.pc.receipt.receiptDetail.service.ReceiptDetailService;
import com.huaheng.pc.receipt.receiptHeader.domain.ReceiptHeader;
import com.huaheng.pc.receipt.receiptHeader.service.ReceiptHeaderService;
import com.huaheng.pc.receipt.workOrderDetail.domain.WorkOrderDetail;
import com.huaheng.pc.receipt.workOrderDetail.service.WorkOrderDetailService;
import com.huaheng.pc.receipt.workOrderHeader.domain.WorkOrderHeader;
import com.huaheng.pc.receipt.workOrderHeader.service.WorkOrderHeaderService;
import com.huaheng.pc.shipment.shipmentContainerDetail.domain.ShipmentContainerDetail;
import com.huaheng.pc.shipment.shipmentContainerDetail.service.ShipmentContainerDetailService;
import com.huaheng.pc.shipment.shipmentContainerHeader.domain.ShipmentCombinationModel;
import com.huaheng.pc.shipment.shipmentContainerHeader.domain.ShipmentContainerHeader;
import com.huaheng.pc.shipment.shipmentContainerHeader.service.ShipmentContainerHeaderService;
import com.huaheng.pc.shipment.shipmentDetail.domain.ShipmentDetail;
import com.huaheng.pc.shipment.shipmentDetail.service.ShipmentDetailService;
import com.huaheng.pc.shipment.shipmentHeader.domain.ShipmentHeader;
import com.huaheng.pc.shipment.shipmentHeader.service.ShipmentHeaderService;
import com.huaheng.pc.task.taskDetail.domain.TaskDetail;
import com.huaheng.pc.task.taskDetail.service.TaskDetailService;
import com.huaheng.pc.task.taskHeader.domain.TaskHeader;
import com.huaheng.pc.task.taskHeader.service.ReceiptTaskService;
import com.huaheng.pc.task.taskHeader.service.TaskHeaderService;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 游杰
 */
@Service
public class MesServiceImpl implements MesService {

    @Resource
    private ReceiptHeaderService receiptHeaderService;
    @Resource
    private ReceiptDetailService receiptDetailService;
    @Resource
    private ShipmentHeaderService shipmentHeaderService;
    @Resource
    private ShipmentDetailService shipmentDetailService;
    @Resource
    private MaterialService materialService;
    @Resource
    private InventoryDetailService inventoryDetailService;
    @Resource
    private InventoryHeaderService inventoryHeaderService;
    @Resource
    private ReceiptContainerHeaderService receiptContainerHeaderService;
    @Resource
    private ShipmentContainerDetailService shipmentContainerDetailService;
    @Resource
    private ContainerService containerService;
    @Resource
    private LocationService locationService;
    @Resource
    private ReceiptContainerDetailService receiptContainerDetailService;
    @Resource
    private ReceiptTaskService receiptTaskService;
    @Resource
    private ShipmentContainerHeaderService shipmentContainerHeaderService;
    @Resource
    private MaterialTypeService materialTypeService;
    @Resource
    private WorkOrderHeaderService workOrderHeaderService;
    @Resource
    private WorkOrderDetailService workOrderDetailService;
    @Resource
    private TaskDetailService taskDetailService;
    @Resource
    private TaskHeaderService taskHeaderService;
    @Resource
    private AddressService addressService;
    @Resource
    private MesService mesService;
    @Resource
    private VehicleTypeService vehicleTypeService;
    @Resource
    private VehicleTypeCoordinateService vehicleTypeCoordinateService;
    @Resource
    private VehicleService vehicleService;
    @Resource
    private SupplierService supplierService;
    @Resource
    private ZoneService zoneService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult receiptOrder(MesOrder mesOrder) {
        List<ReceiptMaterialData> materialDataList = mesOrder.getMaterialDataList();
        BigDecimal totalQty = materialDataList.stream().map(ReceiptMaterialData::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);
        List<String> materialCodeList = materialDataList.stream().map(ReceiptMaterialData::getMaterialCode).distinct().collect(Collectors.toList());
        ReceiptHeader receiptHeader = new ReceiptHeader();
        receiptHeader.setWarehouseCode(QuantityConstant.DEFAULT_WAREHOUSE);
        receiptHeader.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
        receiptHeader.setCode(receiptHeaderService.createCode(mesOrder.getReceiptType()));
        receiptHeader.setReceiptType(mesOrder.getReceiptType());
        receiptHeader.setFirstStatus(QuantityConstant.RECEIPT_HEADER_BUILD);
        receiptHeader.setLastStatus(QuantityConstant.RECEIPT_HEADER_BUILD);
        receiptHeader.setReferCode(mesOrder.getOrderCode());
        receiptHeader.setTotalQty(totalQty);
        receiptHeader.setTotalLines(materialCodeList.size());
        receiptHeader.setOrderCode(mesOrder.getOrderCode());
        receiptHeader.setCreatedBy(StringUtils.isEmpty(ShiroUtils.getLoginName()) ? QuantityConstant.PLATFORM_MES : ShiroUtils.getLoginName());
        receiptHeader.setLastUpdatedBy(StringUtils.isEmpty(ShiroUtils.getLoginName()) ? QuantityConstant.PLATFORM_MES : ShiroUtils.getLoginName());
        boolean success = receiptHeaderService.save(receiptHeader);
        if (!success) {
            throw new ServiceException("入库失败,保存入库单头表失败");
        }
        ReceiptMaterialData receiptMaterialData = materialDataList.stream().findFirst().orElse(null);
        if (receiptMaterialData == null) {
            throw new ServiceException("入库失败,入库物料信息为空");
        }
        Material material = materialService.getByCode(receiptMaterialData.getMaterialCode());
        if (material == null) {
            throw new ServiceException(StringUtils.format("入库失败,根据物料编码 {} 没有找到对应物料信息", receiptMaterialData.getMaterialCode()));
        }
        ReceiptDetail receiptDetail = new ReceiptDetail();
        receiptDetail.setReceiptId(receiptHeader.getId());
        receiptDetail.setReceiptCode(receiptHeader.getCode());
        receiptDetail.setWarehouseCode(QuantityConstant.DEFAULT_WAREHOUSE);
        receiptDetail.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
        receiptDetail.setInventorySts(Boolean.TRUE.equals(receiptMaterialData.getQualityStatus()) ? QuantityConstant.INVENTORY_STATUS_OK : QuantityConstant.INVENTORY_STATUS_NG);
        receiptDetail.setTracingNo(receiptMaterialData.getTracingNo());
        receiptDetail.setQty(totalQty);
        receiptDetail.setTaskQty(totalQty);
        receiptDetail.setMaterialCode(material.getCode());
        receiptDetail.setMaterialName(material.getName());
        receiptDetail.setMaterialSpec(material.getSpec());
        receiptDetail.setMaterialUnit(material.getUnit());
        receiptDetail.setDescription(material.getDescription());
        receiptDetail.setWeight(Objects.isNull(receiptMaterialData.getWeight()) ? material.getWeight() : receiptMaterialData.getWeight());
        receiptDetail.setHigh(material.getHigh());
        receiptDetail.setSupplierCode(receiptMaterialData.getSupplierCode());
        receiptDetail.setSupplierBatch(receiptMaterialData.getSupplierBatch());
        receiptDetail.setSupplierTracingNo(receiptMaterialData.getSupplierTracingNo());
        receiptDetail.setCreatedBy(receiptHeader.getCreatedBy());
        receiptDetail.setLastUpdatedBy(receiptHeader.getLastUpdatedBy());
        success = receiptDetailService.save(receiptDetail);
        if (!success) {
            throw new ServiceException("入库失败,保存入库单明细表失败");
        }
        return AjaxResult.success("创建入库单成功").setData(receiptHeader.getCode());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult shipmentOrder(MesShipmentOrder mesShipmentOrder) {
        String orderCode = mesShipmentOrder.getOrderCode();
        String taskNo = mesShipmentOrder.getTaskNo();
        BigDecimal orderQty = mesShipmentOrder.getOrderQty();
        List<MesOrderMaterial> mesOrderMaterialList = mesShipmentOrder.getMaterialDataList();

        LambdaQueryWrapper<ShipmentHeader> shipmentHeaderQueryWrapper = Wrappers.lambdaQuery();
        ShipmentHeader shipmentHeader = shipmentHeaderService.getOne(shipmentHeaderQueryWrapper);
        if (shipmentHeader != null) {
            if (QuantityConstant.MES_SHIPMENT_BILL_REMOVE.equals("")) {
                boolean sussess = shipmentHeaderService.removeById(shipmentHeader.getId());
                if (!sussess) {
                    throw new ServiceException("取消领料单失败,删除领料单头表失败");
                }
                LambdaQueryWrapper<ShipmentDetail> shipmentDetailQueryWrapper = Wrappers.lambdaQuery();
                shipmentDetailQueryWrapper.eq(ShipmentDetail::getShipmentId, shipmentHeader.getId());
                sussess = shipmentDetailService.remove(shipmentDetailQueryWrapper);
                if (!sussess) {
                    throw new ServiceException("取消领料单失败,删除领料单明细失败");
                }
                /*LambdaQueryWrapper<InventoryHeader> inventoryHeaderQueryWrapper = Wrappers.lambdaQuery();
                inventoryHeaderQueryWrapper.eq(InventoryHeader::getShipmentReferCode, shipmentHeader.getReferCode());
                List<InventoryHeader> headerList = inventoryHeaderService.list(inventoryHeaderQueryWrapper);
                headerList.forEach(it -> it.setShipmentReferCode(""));
                if (StringUtils.isNotEmpty(headerList)) {
                    sussess = inventoryHeaderService.updateBatchById(headerList);
                    if (!sussess) {
                        throw new ServiceException("取消领料单失败,解锁库存失败");
                    }
                }*/
                return AjaxResult.success("取消领料单成功");
            }
            //throw new ServiceException(StringUtils.format("生成领料单失败,领料单:{} 已经存在", shipmentReferCode));
        }

        // TODO 需求数量向上取整,临时用的,等待双单位结论
        mesOrderMaterialList.forEach(mesOrderMaterial -> {
            mesOrderMaterial.setShipmentReferQty(mesOrderMaterial.getShipmentReferQty().setScale(0, RoundingMode.CEILING));
        });

        //工单领料单
        String shipmentType = QuantityConstant.SHIPMENT_BILL_TYPE_OTF;
        String shipmentCode = shipmentHeaderService.createCode(shipmentType);
        shipmentHeader = new ShipmentHeader();
        shipmentHeader.setCode(shipmentCode);
        shipmentHeader.setWarehouseCode(QuantityConstant.DEFAULT_WAREHOUSE);
        shipmentHeader.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
        shipmentHeader.setShipmentType(shipmentType);
        shipmentHeader.setFirstStatus(QuantityConstant.RECEIPT_HEADER_BUILD);
        shipmentHeader.setLastStatus(QuantityConstant.RECEIPT_HEADER_BUILD);
        shipmentHeader.setCreatedBy(QuantityConstant.PLATFORM_MES);
        shipmentHeader.setChidOrderCode(mesShipmentOrder.getChidOrderCode());
        shipmentHeader.setProductCode(mesShipmentOrder.getProductCode());
        shipmentHeader.setTaskNo(taskNo);
        shipmentHeader.setOrderCode(orderCode);
        shipmentHeader.setOrderQty(orderQty);
        shipmentHeader.setTotalLines(mesOrderMaterialList.size());
        shipmentHeader.setTotalQty(mesOrderMaterialList.stream().map(MesOrderMaterial::getShipmentReferQty).reduce(BigDecimal.ZERO, BigDecimal::add));
        if (!shipmentHeaderService.save(shipmentHeader)) {
            throw new ServiceException("生成领料单失败,保存领料单失败");
        }

        List<ShipmentDetail> shipmentDetailList = new LinkedList<>();
        for (MesOrderMaterial mesOrderMaterial : mesOrderMaterialList) {
            Material material = materialService.getByCode(mesOrderMaterial.getMaterialCode());
            if (material == null) {
                throw new ServiceException(StringUtils.format("生成领料单失败,物料:{} 不存在", mesOrderMaterial.getMaterialCode()));
            }
            // 物料是原材料就冻结库存
            /*if (Boolean.TRUE.equals(mesOrderMaterial.getIsMaterial())) {
                // 查询原材料可用库存
                List<InventoryDetail> inventoryList = inventoryDetailService.getUsableByMaterialCodeAndOrderCode(mesOrderMaterial.getMaterialCode(), orderCode);
                // 冻结库存
                mesService.lockRawInventory(material, inventoryList, mesOrderMaterial, shipmentHeader, orderCode);
            }*/
            /*LambdaQueryWrapper<InventoryDetail> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(InventoryDetail::getMaterialCode, material.getCode())
                    .eq(InventoryDetail::getOrderCode, orderCode)
                    .apply("qty != taskQty and TIMESTAMPDIFF(HOUR, created, now()) >= solidifyTime");
            List<InventoryDetail> inventoryDetailList = inventoryDetailService.list(queryWrapper);
            if (StringUtils.isEmpty(inventoryDetailList)) {
                throw new ServiceException(StringUtils.format("生成领料单失败,工单:{} 下的物料:{} 可用数量不足", orderCode, material.getCode()));
            }*/
            List<ShipmentDetail> shipmentDetails = createShipmentDetailByQty(material, mesOrderMaterial, shipmentHeader);
            shipmentDetailList.addAll(shipmentDetails);
        }
        if (!shipmentDetailService.saveBatch(shipmentDetailList)) {
            throw new ServiceException("生成领料单失败,保存领料单明细失败");
        }
        return AjaxResult.success("创建领料库单成功");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void lockRawInventory(Material material, List<InventoryDetail> inventoryList,
                                 MesOrderMaterial mesOrderMaterial, ShipmentHeader shipmentHeader, String orderCode) {
        // 1、数量校验
        if (StringUtils.isEmpty(inventoryList)) {
            throw new ServiceException(StringUtils.format("工单:{} 下的物料 {},{} 暂无可用库存", orderCode, material.getCode(), material.getName()));
        }
        BigDecimal invSumQty = inventoryList.stream()
                .map(InventoryDetail::getQty)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal invSumTaskQty = inventoryList.stream()
                .map(InventoryDetail::getTaskQty)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        // 可用数量和出库需求数量作比较
        BigDecimal availableQty = invSumQty.subtract(invSumTaskQty);
        BigDecimal shipmentReferQty = mesOrderMaterial.getShipmentReferQty().setScale(0, RoundingMode.CEILING);
        if (availableQty.compareTo(BigDecimal.ZERO) <= -1 || availableQty.compareTo(shipmentReferQty) <= -1) {
            throw new ServiceException(StringUtils.format("物料 [{},{}] 可用数量不足,当前可用数量: {}",
                    material.getCode(), material.getName(), availableQty));
        }
        // 2、优先 先进先出原则,冻结库存
        List<InventoryDetail> invList = new LinkedList<>();
        for (InventoryDetail inventoryDetail : inventoryList) {
            BigDecimal qty = inventoryDetail.getQty();
            if (shipmentReferQty.compareTo(BigDecimal.ZERO) == 0) {
                break;
            }
            if (shipmentReferQty.subtract(qty).compareTo(BigDecimal.ZERO) >= 0) {
                shipmentReferQty = shipmentReferQty.subtract(qty);
                invList.add(inventoryDetail);
            }
        }
        // 冻结一个即冻结整盘
        // 根据确认数量后的库存明细 获取对应的库存头表id,再查询该库存头表下的所有库存明细,接着过滤出与当前物料一致的库存明细,最后冻结
        List<Integer> inventoryHeaderIdList = invList.stream().map(InventoryDetail::getInventoryHeaderId).distinct().collect(Collectors.toList());
        inventoryHeaderIdList.forEach(headerId -> {
            InventoryHeader inventoryHeader = new InventoryHeader();
            inventoryHeader.setId(headerId);
            //inventoryHeader.setShipmentReferCode(shipmentHeader.getReferCode());
            inventoryHeader.setLastUpdatedBy(QuantityConstant.PLATFORM_MES);
            inventoryHeader.setCreatedBy(QuantityConstant.PLATFORM_MES);
            if (!inventoryHeaderService.updateById(inventoryHeader)) {
                throw new ServiceException("领料单下发失败,冻结库存头表失败");
            }
        });
        List<InventoryDetail> inventoryDetailList = inventoryDetailService.getByHeaderIds(inventoryHeaderIdList);
        List<InventoryDetail> inventoryDetails = inventoryDetailList.stream().
                filter(detail -> detail.getMaterialCode().equals(material.getCode()))
                .collect(Collectors.toList());
        if (!inventoryDetailService.updateBatchById(inventoryDetails)) {
            throw new ServiceException("领料单下发失败,冻结库存明细失败");
        }
    }

    public List<ShipmentDetail> createShipmentDetailByQty(Material material, MesOrderMaterial mesOrderMaterial, ShipmentHeader shipmentHeader) {
        List<ShipmentDetail> shipmentDetailList = new LinkedList<>();
        ShipmentDetail shipmentDetail = new ShipmentDetail();
        shipmentDetail.setShipmentId(shipmentHeader.getId());
        shipmentDetail.setShipmentCode(shipmentHeader.getCode());
        shipmentDetail.setWarehouseCode(shipmentHeader.getWarehouseCode());
        shipmentDetail.setCompanyCode(shipmentHeader.getCompanyCode());
        shipmentDetail.setQty(mesOrderMaterial.getShipmentReferQty());
        shipmentDetail.setMaterialCode(material.getCode());
        shipmentDetail.setMaterialName(material.getName());
        shipmentDetail.setMaterialSpec(material.getSpec());
        shipmentDetail.setMaterialUnit(material.getUnit());
        //shipmentDetail.setAuxiliaryUnit(material.getAuxiliaryUnit());
        shipmentDetail.setWeight(material.getWeight());
        // MES下发领料单时,物料没有库存状态,故默认且只能为合格品
        shipmentDetail.setInventorySts(QuantityConstant.INVENTORY_STATUS_OK);
        shipmentDetail.setCreatedBy(QuantityConstant.PLATFORM_MES);
        shipmentDetailList.add(shipmentDetail);
        return shipmentDetailList;
    }

    /*@Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult shipmentProduct(MesShipmentProduct mesShipmentProduct) {
        String taskNo = mesShipmentProduct.getTaskNo();
        String orderCode = mesShipmentProduct.getOrderCode();
        String containerCode = mesShipmentProduct.getContainerCode();
        String vehicleCode = mesShipmentProduct.getVehicleCode();
        String fromPort = mesShipmentProduct.getFromPort();
        String toPort = mesShipmentProduct.getToPort();
        List<MaterialData> materialDataList = mesShipmentProduct.getMaterialDataList();

        return workTaskService.createOverStation(containerCode, vehicleCode, fromPort, toPort, taskNo, orderCode, materialDataList);
    }*/

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult createOverStation(MesOverStationDto mesOverStationDto) {
        String containerCode = mesOverStationDto.getContainerCode();
        Container container = containerService.getByCode(containerCode);
        if (container == null) {
            throw new ServiceException(StringUtils.format("换站失败,根据载具编码:{} 未找到载具", containerCode));
        }
        if (QuantityConstant.STATUS_LOCATION_LOCK.equals(container.getStatus())) {
            throw new ServiceException(StringUtils.format("换站失败,载具:{} 已锁定", container.getCode()));
        }
        String vehicleCode = mesOverStationDto.getVehicleCode();
        Vehicle vehicle = vehicleService.getByCode(vehicleCode);
        if (vehicle == null) {
            throw new ServiceException(StringUtils.format("换站失败,根据盛具编码:{} 未找到盛具", vehicleCode));
        }
        if (StringUtils.isNotEmpty(container.getVehicleCode())) {
            throw new ServiceException(StringUtils.format("换站失败,载具:{} 已和盛具:{} 绑定", container.getCode(), vehicle.getCode()));
        }
        String fromPort = mesOverStationDto.getFromPort();
        String toPort = mesOverStationDto.getToPort();
        String taskNo = mesOverStationDto.getTaskNo();
        List<MaterialData> materialDataList = mesOverStationDto.getMaterialDataList();
        //String orderCode = mesOverStationDto.getOrderCode();

        boolean success = taskHeaderService.createOverStationTask(container, vehicle, fromPort, toPort,
                taskNo, materialDataList, QuantityConstant.MES_TASK_NOT_BACK);
        if (!success) {
            throw new ServiceException("换站失败,创建换站任务失败");
        }
        // 锁定载具
        success = containerService.updateStatus(container.getCode(), QuantityConstant.STATUS_CONTAINER_LOCK);
        if (!success) {
            throw new ServiceException("换站失败,锁定载具失败");
        }
        // 载具和盛具绑定
        success = containerService.updateVehicle(container.getCode(), vehicle.getCode(), vehicle.getVehicleType());
        if (!success) {
            throw new ServiceException("换站失败,载具与盛具绑定失败");
        }

        // 如果换站任务携带物料信息 且 shipmentReferCode 不为空。
        // 需要进行数据闭环:1、生成该物料信息的半成品入库单 2、根据 shipmentReferCode 找到对应的出库单,更新对应的明细
        /*if (StringUtils.isNotEmpty(materialDataList) && StringUtils.isNotEmpty(shipmentReferCode)) {
            // 创建半成品入库单
            receiptHeaderService.createSemiBillByOverStation(materialDataList, QuantityConstant.RECEIPT_TYPE_SEMI,
                    shipmentReferCode);
            // 补全领料单
            shipmentHeaderService.completeShipmentReferCodeByOverStation(shipmentReferCode, materialDataList);
        }*/
        return AjaxResult.success("创建换站任务成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult wmsReceipt(MesReceipt mesReceipt) {
        String orderCode = mesReceipt.getOrderCode();
        MesOrder mesOrder = new MesOrder();
        mesOrder.setOrderCode(orderCode);
        mesOrder.setTaskNo(mesReceipt.getTaskNo());
        mesOrder.setMaterialDataList(mesReceipt.getReceiptMaterialDataList());
        mesOrder.setReceiptType(mesReceipt.getReceiptType());
        AjaxResult ajaxResult = mesService.receiptOrder(mesOrder);
        if (Boolean.TRUE.equals(ajaxResult.hasErr())) {
            throw new ServiceException(ajaxResult.getMsg());
        }
        Map<String, List<ReceiptMaterialData>> collect = mesReceipt.getReceiptMaterialDataList()
                .stream().collect(Collectors.groupingBy(ReceiptMaterialData::getVehicleCode));
        for (Map.Entry<String, List<ReceiptMaterialData>> receiptDataEntry : collect.entrySet()) {
            String vehicleCode = receiptDataEntry.getKey();
            List<ReceiptMaterialData> receiptMaterialDataList = receiptDataEntry.getValue();
            // 生成虚拟载具用于组盘
            Container virtualContainer = containerService.createVirtualContainer();
            // 校验盛具
            Vehicle vehicle = vehicleService.getVehicleByCode(vehicleCode, QuantityConstant.DEFAULT_WAREHOUSE);
            if (StringUtils.isNull(vehicle)) {
                throw new ServiceException(StringUtils.format("组盘失败,不存在盛具编码 {}", vehicleCode));
            }
            Container realContainer = containerService.getByVehicleCode(vehicle.getCode());
            if (realContainer != null) {
                throw new ServiceException(StringUtils.format("组盘失败,盛具:{} 已和载具:{} 绑定", vehicle.getCode(), realContainer.getCode()));
            }
            boolean success;
            // 新建保存组盘头表记录
            LambdaQueryWrapper<ReceiptContainerHeader> receiptContainerHeaderQueryWrapper = Wrappers.lambdaQuery();
            receiptContainerHeaderQueryWrapper.eq(ReceiptContainerHeader::getVehicleCode, vehicleCode)
                    .eq(ReceiptContainerHeader::getStatus, QuantityConstant.RECEIPT_CONTAINER_BUILD);
            ReceiptContainerHeader receiptContainerHeader = receiptContainerHeaderService.getOne(receiptContainerHeaderQueryWrapper);

            // 若组过盘则删除原来的组盘记录和单据
            if (StringUtils.isNotNull(receiptContainerHeader)) {
                if (receiptContainerHeader.getStatus() >= QuantityConstant.RECEIPT_CONTAINER_TASK && receiptContainerHeader.getStatus() < QuantityConstant.RECEIPT_CONTAINER_FINISHED) {
                    throw new ServiceException("组盘失败,载具已经生成任务不能放物料了");
                }
                success = receiptContainerHeaderService.removeById(receiptContainerHeader);
                if (!success) {
                    throw new ServiceException("重新组盘失败,删除组盘头表失败");
                }
                List<ReceiptContainerDetail> receiptContainerDetailList = receiptContainerDetailService.getByHeaderId(receiptContainerHeader.getId());
                List<Integer> receiptContainerDetailIdList = receiptContainerDetailList.stream()
                        .map(ReceiptContainerDetail::getId)
                        .collect(Collectors.toList());
                success = receiptContainerDetailService.removeByIds(receiptContainerDetailIdList);
                if (!success) {
                    throw new ServiceException("重新组盘失败,删除组盘明细失败");
                }
                List<Integer> receiptHeaderIdList = receiptContainerDetailList.stream()
                        .map(ReceiptContainerDetail::getReceiptId)
                        .distinct()
                        .collect(Collectors.toList());
                for (Integer receiptHeaderId : receiptHeaderIdList) {
                    success = receiptHeaderService.removeById(receiptHeaderId);
                    if (!success) {
                        throw new ServiceException("重新组盘失败,删除单据头表失败");
                    }
                    LambdaQueryWrapper<ReceiptDetail> queryWrapper = Wrappers.lambdaQuery();
                    queryWrapper.eq(ReceiptDetail::getReceiptId, receiptHeaderId);
                    success = receiptDetailService.remove(queryWrapper);
                    if (!success) {
                        throw new ServiceException("重新组盘失败,删除单据明细失败");
                    }
                }
            }

            // 组盘后,将虚拟载具和盛具绑定
            if (!containerService.updateVehicle(virtualContainer.getCode(), vehicle.getCode(), vehicle.getVehicleType())) {
                throw new ServiceException("传爆管入库组盘失败,虚拟载具与盛具绑定失败");
            }

            String orderNumber = mesReceipt.getOrderNumber();
            String batchNumber = mesReceipt.getBatchNumber();
            // 根据工单号查询工单物料明细,校验此次入库的物料是否在工单明细清单中,不在则提示异常。
            WorkOrderHeader workOrderHeader = workOrderHeaderService.getOrderByCode(orderCode);
            if (workOrderHeader == null) {
                throw new ServiceException(StringUtils.format("组盘失败,根据工单号:{} 未找到工单"));
            }
            if (!QuantityConstant.WORK_ORDER_BUILD.equals(workOrderHeader.getOrderStatus())
                    && !QuantityConstant.WORK_ORDER_RELEASED.equals(workOrderHeader.getOrderStatus())
                    && !QuantityConstant.WORK_ORDER_SENDWORK.equals(workOrderHeader.getOrderStatus())
                    && !QuantityConstant.WORK_ORDER_PRODUCTION.equals(workOrderHeader.getOrderStatus())) {
                throw new ServiceException(StringUtils.format("组盘失败,工单:{} 非新建状态,不可进行入库操作", workOrderHeader.getOrderCode()));
            }
            List<WorkOrderDetail> workOrderDetailList = workOrderDetailService.getByHeaderId(workOrderHeader.getId());
            if (StringUtils.isEmpty(workOrderDetailList)) {
                throw new ServiceException(StringUtils.format(StringUtils.format("组盘失败,工单:{} 下没有物料明细", orderCode)));
            }
            List<String> materialCodeList = workOrderDetailList.stream().map(WorkOrderDetail::getMaterialCode).collect(Collectors.toList());
            for (ReceiptMaterialData data : receiptMaterialDataList) {
                Material material = materialService.getMaterialByCode(data.getMaterialCode(), QuantityConstant.DEFAULT_WAREHOUSE);
                if (material == null) {
                    throw new ServiceException(StringUtils.format("组盘失败,根据物料编码:{} 没有找到对应物料信息", data.getMaterialCode()));
                }
                if (!materialCodeList.contains(material.getCode())) {
                    throw new ServiceException(StringUtils.format("组盘失败,工单:{} 下的物料明细中不存在入库物料:{}",
                            orderCode, material.getCode()));
                }
                if (StringUtils.isNull(data.getLocationNoX())) {
                    throw new ServiceException("组盘失败,物料有 x 坐标为空的,请检查");
                }
                if (StringUtils.isNull(data.getLocationNoY())) {
                    throw new ServiceException("组盘失败,物料有 y 坐标为空的,请检查");
                }
                String locationNoX = data.getLocationNoX().toString();
                String locationNoY = data.getLocationNoY().toString();
                if (StringUtils.isNull(data.getQty()) || data.getQty().equals(BigDecimal.ZERO)) {
                    throw new ServiceException(StringUtils.format("组盘失败,坐标 {},{} 的物料数量为空或为0", locationNoX
                            , locationNoY));
                }
                if (StringUtils.isEmpty(data.getTracingNo())) {
                    throw new ServiceException(StringUtils.format("组盘失败,坐标:{},{} 的物料追溯码为空",
                            locationNoX, locationNoY));
                }
                if (StringUtils.isNull(data.getQualityStatus())) {
                    throw new ServiceException(StringUtils.format("组盘失败,坐标:{},{} 的物料质量状态为空",
                            locationNoX, locationNoY));
                }
                // 校验物料绑定的盛具类型
                if (!vehicle.getVehicleType().equals(material.getVehicleTypeCode())) {
                    throw new ServiceException(StringUtils.format("组盘失败,入库盛具类型 {} 与物料 {} 绑定的盛具类型 {} 不一致",
                            vehicle.getVehicleType(), material.getName(), material.getVehicleTypeCode()));
                }
                // 校验物料是否入立库(根据是否物料分区判断)
                if (StringUtils.isEmpty(material.getType())) {
                    throw new ServiceException(StringUtils.format("组盘失败,物料:{} 未设置物料类别", material.getName()));
                }
                MaterialType materialType = materialTypeService.getByCode(material.getType());
                if (materialType == null) {
                    throw new ServiceException(StringUtils.format("组盘失败,物料类别编码 {} 不存在", material.getType()));
                }
                String materialAreaCode = materialType.getMaterialAreaCode();
                if (StringUtils.isEmpty(materialAreaCode)) {
                    throw new ServiceException(StringUtils.format("组盘失败,物料:{} 所属类别:{} 未配置物料分区", material.getName(), materialType.getName()));
                }
            }
            // 校验同一个xy坐标的物料 数量只能为1
            if (!QuantityConstant.VEHICLE_TYPE_COMMON.equals(vehicle.getVehicleType())) {
                ReceiptMaterialData qtyNotZeroMaterial = receiptMaterialDataList.stream()
                        .filter(x -> x.getQty().compareTo(BigDecimal.ONE) != 0)
                        .findAny()
                        .orElse(null);
                if (qtyNotZeroMaterial != null) {
                    throw new ServiceException(StringUtils.format("组盘失败,物料坐标 {},{} 的数量异常,请检查数量",
                            qtyNotZeroMaterial.getLocationNoX(),
                            qtyNotZeroMaterial.getLocationNoY()));
                }
                Map<String, List<ReceiptMaterialData>> materialGroupByXyMap = receiptMaterialDataList.stream()
                        .collect(Collectors.groupingBy(material -> material.getLocationNoX().toString().concat(",").concat(material.getLocationNoY().toString())));
                for (Map.Entry<String, List<ReceiptMaterialData>> entry : materialGroupByXyMap.entrySet()) {
                    if (entry.getValue().size() > 1) {
                        throw new ServiceException(StringUtils.format("组盘失败,物料坐标 {} 重复,请检查坐标", entry.getKey()));
                    }
                }
            }

            LambdaQueryWrapper<ReceiptHeader> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(ReceiptHeader::getCode, ajaxResult.getData());
            ReceiptHeader receiptHeader = receiptHeaderService.getOne(wrapper);
            if (receiptHeader == null) {
                throw new ServiceException(StringUtils.format("组盘失败,未找到上游单号为:{} 的入库单", mesReceipt.getTaskNo()));
            }
            //入库单id查询入库单详情
            LambdaQueryWrapper<ReceiptDetail> receiptDetailQueryWrapper = Wrappers.lambdaQuery();
            receiptDetailQueryWrapper.eq(ReceiptDetail::getReceiptId, receiptHeader.getId());
            List<ReceiptDetail> receiptDetailList = receiptDetailService.list(receiptDetailQueryWrapper);
            if (StringUtils.isEmpty(receiptDetailList)) {
                throw new ServiceException(StringUtils.format("组盘失败,未找到入库单:{} 的详情", receiptHeader.getId()));
            }
            receiptContainerHeader = new ReceiptContainerHeader();
            receiptContainerHeader.setReceiptCode(ajaxResult.getData().toString());
            receiptContainerHeader.setWarehouseCode(QuantityConstant.DEFAULT_WAREHOUSE);
            receiptContainerHeader.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
            receiptContainerHeader.setContainerCode(virtualContainer.getCode());
            receiptContainerHeader.setTaskType(QuantityConstant.TASK_TYPE_WHOLERECEIPT);
            receiptContainerHeader.setCreatedBy(QuantityConstant.PLATFORM_MES);
            receiptContainerHeader.setLastUpdatedBy(QuantityConstant.PLATFORM_MES);
            receiptContainerHeader.setOrderCode(orderCode);
            receiptContainerHeader.setOrderNumber(orderNumber);
            receiptContainerHeader.setBatchNumber(batchNumber);
            receiptContainerHeader.setVehicleCode(vehicle.getCode());
            if (!receiptContainerHeaderService.save(receiptContainerHeader)) {
                throw new ServiceException("组盘失败,入库组盘头表保存失败");
            }
            List<ReceiptContainerDetail> detailList = new ArrayList<>();
            //入库组盘详情
            for (ReceiptMaterialData data : receiptMaterialDataList) {
                Material material = materialService.getByCode(data.getMaterialCode());
                ReceiptContainerDetail receiptContainerDetail = new ReceiptContainerDetail();
                receiptContainerDetail.setReceiptContainerId(receiptContainerHeader.getId());
                receiptContainerDetail.setWarehouseCode(receiptContainerHeader.getWarehouseCode());
                ReceiptDetail receiptDetail = receiptDetailList.stream().filter(x -> x.getMaterialCode().equals(data.getMaterialCode())).findFirst().orElse(null);
                if (receiptDetail == null) {
                    throw new ServiceException(StringUtils.format("入库失败,入库单:{} 明细中不存在物料:{}", receiptHeader.getCode(), data.getMaterialCode()));
                }
                receiptContainerDetail.setReceiptId(receiptDetail.getReceiptId());
                receiptContainerDetail.setReceiptDetailId(receiptDetail.getId());
                receiptContainerDetail.setReceiptCode(receiptDetail.getReceiptCode());
                receiptContainerDetail.setSupplierCode(data.getSupplierCode());
                receiptContainerDetail.setSupplierBatch(data.getSupplierBatch());
                receiptContainerDetail.setSupplierTracingNo(data.getSupplierTracingNo());
                receiptContainerDetail.setLocationNoX(data.getLocationNoX());
                receiptContainerDetail.setLocationNoY(data.getLocationNoY());
                receiptContainerDetail.setTracingNo(data.getTracingNo());
                receiptContainerDetail.setFlowCode(data.getFlowCode());
                receiptContainerDetail.setTaskType(receiptContainerHeader.getTaskType());
                receiptContainerDetail.setContainerCode(receiptContainerHeader.getContainerCode());
                receiptContainerDetail.setCompanyCode(receiptContainerHeader.getCompanyCode());
                receiptContainerDetail.setMaterialCode(data.getMaterialCode());
                receiptContainerDetail.setMaterialName(material.getName());
                receiptContainerDetail.setMaterialSpec(material.getSpec());
                receiptContainerDetail.setMaterialUnit(material.getUnit());
                receiptContainerDetail.setDescription(material.getDescription());
                receiptContainerDetail.setWeight(receiptDetail.getWeight());
                receiptContainerDetail.setHigh(material.getHigh());
                receiptContainerDetail.setSolidifyTime(data.getSolidifyTime());
                receiptContainerDetail.setQty(data.getQty());
                receiptContainerDetail.setInventorySts(Boolean.TRUE.equals(data.getQualityStatus()) ? QuantityConstant.INVENTORY_STATUS_OK : QuantityConstant.INVENTORY_STATUS_NG);
                detailList.add(receiptContainerDetail);

                ReceiptDetail updateReceiptDetail = new ReceiptDetail();
                updateReceiptDetail.setId(receiptDetail.getId());
                updateReceiptDetail.setProcessStamp(String.valueOf(QuantityConstant.RECEIPT_HEADER_WAIT));
                if (!receiptDetailService.updateById(updateReceiptDetail)) {
                    throw new ServiceException("入库失败,更新入库单详情失败");
                }
                //更新头表状态
                receiptDetailService.updateReceiptHeaderLastStatus(receiptDetail.getReceiptId());
            }
            if (!receiptContainerDetailService.saveBatch(detailList)) {
                throw new ServiceException("组盘失败,保存入库组盘详情失败");
            }
        }
        return AjaxResult.success("组盘成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult receipt(MesReceipt mesReceipt) {
        // 校验载具
        String containerCode = mesReceipt.getContainerCode();
        if (StringUtils.isEmpty(containerCode)) {
            throw new ServiceException("入库失败,载具编码为空");
        }
        Container container = containerService.getContainerByCode(containerCode, QuantityConstant.DEFAULT_WAREHOUSE);
        if (StringUtils.isNull(container)) {
            throw new ServiceException(StringUtils.format("入库失败,载具编码:{} 不存在", containerCode));
        }
        Location location = locationService.getLocationByContainerCode(containerCode);
        if (location != null) {
            throw new ServiceException(StringUtils.format("入库失败,载具:{} 已在库位:{} 上", containerCode, location.getCode()));
        }
        InventoryHeader inventoryHeader = inventoryHeaderService.getByContainerCode(containerCode);
        if (inventoryHeader != null) {
            throw new ServiceException(StringUtils.format("入库失败,载具:{} 已经存在库存", containerCode));
        }
        // 校验盛具
        Vehicle vehicle = vehicleService.getVehicleByCode(mesReceipt.getVehicleCode(), QuantityConstant.DEFAULT_WAREHOUSE);
        if (StringUtils.isNull(vehicle)) {
            throw new ServiceException(StringUtils.format("入库失败,不存在盛具编码:{}", mesReceipt.getVehicleCode()));
        }
        if (StringUtils.isNotEmpty(container.getVehicleCode()) &&
                !container.getVehicleCode().equals(mesReceipt.getVehicleCode())) {
            throw new ServiceException(StringUtils.format("入库失败,载具:{} 已和盛具:{} 绑定",
                    container.getCode(), container.getVehicleCode()));
        }
        Container containerByVehicleCode = containerService.getByVehicleCode(mesReceipt.getVehicleCode());
        if (containerByVehicleCode != null) {
            throw new ServiceException(StringUtils.format("入库失败,盛具:{} 已和载具:{} 绑定",
                    mesReceipt.getVehicleCode(), containerByVehicleCode.getCode()));
        }
        List<ReceiptMaterialData> receiptMaterialDataList = mesReceipt.getReceiptMaterialDataList();
        receiptMaterialDataList = receiptMaterialDataList.stream().filter(x -> Boolean.TRUE.equals(x.getIsMaterial()))
                .collect(Collectors.toList());
        if (receiptMaterialDataList.isEmpty()) {
            throw new ServiceException("入库失败,没有有效的入库物料信息");
        }
        for (ReceiptMaterialData data : receiptMaterialDataList) {
            Material material = materialService.getMaterialByCode(data.getMaterialCode(), QuantityConstant.DEFAULT_WAREHOUSE);
            if (material == null) {
                throw new ServiceException(StringUtils.format("入库失败,根据物料编码:{} 没有找到对应物料信息", data.getMaterialCode()));
            }
            if (StringUtils.isNull(data.getLocationNoX())) {
                throw new ServiceException("入库失败,物料有 x 坐标为空的,请检查");
            }
            if (StringUtils.isNull(data.getLocationNoY())) {
                throw new ServiceException("入库失败,物料有 y 坐标为空的,请检查");
            }
            if (StringUtils.isNull(data.getQty()) || data.getQty().equals(BigDecimal.ZERO)) {
                throw new ServiceException(StringUtils.format("入库失败,坐标:{},{} 的物料数量为空或为0",
                        data.getLocationNoX().toString(), data.getLocationNoY().toString()));
            }
            if (StringUtils.isEmpty(data.getTracingNo())) {
                throw new ServiceException(StringUtils.format("入库失败,坐标:{},{} 的物料追溯码为空",
                        data.getLocationNoX().toString(), data.getLocationNoY().toString()));
            }
            if (StringUtils.isNull(data.getQualityStatus())) {
                throw new ServiceException(StringUtils.format("入库失败,坐标:{},{} 的物料质量状态为空",
                        data.getLocationNoX().toString(), data.getLocationNoY().toString()));
            }
            // 校验物料绑定的盛具类型
            if (!vehicle.getVehicleType().equals(material.getVehicleTypeCode())) {
                throw new ServiceException(StringUtils.format("入库失败,入库盛具类型:{} 与物料:{} 绑定的盛具类型:{} 不一致",
                        vehicle.getVehicleType(), material.getName(), material.getVehicleTypeCode()));
            }
            if (StringUtils.isEmpty(material.getType())) {
                throw new ServiceException(StringUtils.format("入库失败,物料:{} 未设置物料类别", material.getName()));
            }
            MaterialType materialType = materialTypeService.getByCode(material.getType());
            if (materialType == null) {
                throw new ServiceException(StringUtils.format("入库失败,物料类别编码:{} 不存在", material.getType()));
            }
            String materialAreaCode = materialType.getMaterialAreaCode();
            if (StringUtils.isEmpty(materialAreaCode)) {
                throw new ServiceException(StringUtils.format("入库失败,物料:{} 所属类别:{} 未配置物料分区", material.getName(), materialType.getName()));
            }
        }

        // 校验同一个xy坐标的物料 数量只能为1
        List<ReceiptMaterialData> materialDataList = receiptMaterialDataList;
        if (!QuantityConstant.VEHICLE_TYPE_COMMON.equals(vehicle.getVehicleType())) {
            ReceiptMaterialData qtyNotZeroMaterial = materialDataList.stream()
                    .filter(x -> x.getQty().compareTo(BigDecimal.ONE) != 0)
                    .findAny()
                    .orElse(null);
            if (StringUtils.isNotNull(qtyNotZeroMaterial)) {
                throw new ServiceException(StringUtils.format("入库失败,物料坐标:{},{} 的数量异常,请检查数量",
                        qtyNotZeroMaterial.getLocationNoX(),
                        qtyNotZeroMaterial.getLocationNoY()));
            }
            Map<String, List<ReceiptMaterialData>> materialGroupByXyMap = materialDataList.stream()
                    .collect(Collectors.groupingBy(material -> material.getLocationNoX().toString().concat(",").concat(material.getLocationNoY().toString())));
            for (Map.Entry<String, List<ReceiptMaterialData>> entry : materialGroupByXyMap.entrySet()) {
                if (entry.getValue().size() > 1) {
                    throw new ServiceException(StringUtils.format("入库失败,物料坐标:{} 重复,请检查坐标", entry.getKey()));
                }
            }
        }
        MesOrder mesOrder = new MesOrder();
        mesOrder.setTaskNo(mesReceipt.getTaskNo());
        mesOrder.setOrderCode(mesReceipt.getOrderCode());
        mesOrder.setMaterialDataList(receiptMaterialDataList);
        mesOrder.setReceiptType(mesReceipt.getReceiptType());
        // 手动生成入库单
        AjaxResult ajaxResult = mesService.receiptOrder(mesOrder);
        if (Boolean.TRUE.equals(ajaxResult.hasErr())) {
            throw new ServiceException(ajaxResult.getMsg());
        }
        // 保存组盘头表
        // 根据载具编码查询组盘表头记录
        LambdaQueryWrapper<ReceiptContainerHeader> lambda = Wrappers.lambdaQuery();
        lambda.eq(ReceiptContainerHeader::getContainerCode, mesReceipt.getContainerCode())
                .eq(ReceiptContainerHeader::getStatus, QuantityConstant.RECEIPT_CONTAINER_BUILD);
        ReceiptContainerHeader receiptContainerHeader = receiptContainerHeaderService.getOne(lambda);

        if (StringUtils.isNull(receiptContainerHeader)) {
            receiptContainerHeader = new ReceiptContainerHeader();
            receiptContainerHeader.setReceiptCode(ajaxResult.getData().toString());
            receiptContainerHeader.setWarehouseCode(QuantityConstant.DEFAULT_WAREHOUSE);
            receiptContainerHeader.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
            receiptContainerHeader.setContainerCode(container.getCode());
            receiptContainerHeader.setTaskType(QuantityConstant.TASK_TYPE_WHOLERECEIPT);
            receiptContainerHeader.setCreatedBy(QuantityConstant.PLATFORM_MES);
            receiptContainerHeader.setLastUpdatedBy(QuantityConstant.PLATFORM_MES);
            receiptContainerHeader.setTaskNo(mesReceipt.getTaskNo());
            receiptContainerHeader.setOrderCode(mesReceipt.getOrderCode());
            receiptContainerHeader.setVehicleCode(vehicle.getCode());
            receiptContainerHeader.setPort(mesReceipt.getFromPort());

            WorkOrderHeader workOrderHeader = workOrderHeaderService.getOrderByCode(mesReceipt.getOrderCode());
            if (workOrderHeader != null) {
                receiptContainerHeader.setBatchNumber(workOrderHeader.getBatchNumber());
                receiptContainerHeader.setOrderNumber(workOrderHeader.getOrderNumber());
            }
            if (!receiptContainerHeaderService.save(receiptContainerHeader)) {
                throw new ServiceException("入库失败,入库组盘头表保存失败");
            }
            // 组盘的时候,将载具和盛具绑定
            if (!containerService.updateVehicle(container.getCode(), vehicle.getCode(), vehicle.getVehicleType())) {
                throw new ServiceException("入库失败,载具与盛具绑定失败");
            }
        } else {
            if (receiptContainerHeader.getStatus() >= QuantityConstant.RECEIPT_CONTAINER_TASK && receiptContainerHeader.getStatus() < QuantityConstant.RECEIPT_CONTAINER_FINISHED) {
                throw new ServiceException("入库失败,载具已经生成任务,不能放物料了!");
            }
        }
        LambdaQueryWrapper<ReceiptHeader> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ReceiptHeader::getCode, ajaxResult.getData());
        ReceiptHeader receiptHeader = receiptHeaderService.getOne(wrapper);
        if (receiptHeader == null) {
            throw new ServiceException(StringUtils.format("入库失败,未找到上游单号为:{} 的入库单", mesReceipt.getTaskNo()));
        }
        //入库单id查询入库单详情
        LambdaQueryWrapper<ReceiptDetail> receiptDetailQueryWrapper = Wrappers.lambdaQuery();
        receiptDetailQueryWrapper.eq(ReceiptDetail::getReceiptId, receiptHeader.getId());
        List<ReceiptDetail> receiptDetailList = receiptDetailService.list(receiptDetailQueryWrapper);
        if (StringUtils.isEmpty(receiptDetailList)) {
            throw new ServiceException(StringUtils.format("入库失败,未找到入库单:{} 的详情", receiptHeader.getId()));
        }
        //入库单详情的物料条码比较物料入库的物料条码
        for (ReceiptDetail receiptDetail : receiptDetailList) {
            //更新入库单详情的收货数量
            if (!receiptDetailService.updateById(receiptDetail)) {
                throw new ServiceException("入库失败,更新入库单详情失败");
            }
            //如果单据数量等于已收数量,更新入库详情状态和入库单状态
            if (receiptDetail.getQty().compareTo(receiptDetail.getTaskQty()) == 0) {
                receiptDetail.setProcessStamp(QuantityConstant.RECEIPT_HEADER_RECEIVING.toString());
                if (!receiptDetailService.updateById(receiptDetail)) {
                    throw new ServiceException("入库失败,更新入库详情处理标记失败");
                }
            }
            receiptDetailService.updateReceiptHeaderLastStatus(receiptDetail.getReceiptId());
        }
        List<ReceiptContainerDetail> detailList = new ArrayList<>();
        for (ReceiptMaterialData data : materialDataList) {
            // 没有物料不算库存
            if (Boolean.FALSE.equals(data.getIsMaterial())) {
                continue;
            }
            Material material = materialService.getMaterialByCode(data.getMaterialCode(), QuantityConstant.DEFAULT_WAREHOUSE);
            ReceiptContainerDetail receiptContainerDetail = new ReceiptContainerDetail();
            receiptContainerDetail.setReceiptContainerId(receiptContainerHeader.getId());
            receiptContainerDetail.setWarehouseCode(receiptContainerHeader.getWarehouseCode());
            ReceiptDetail receiptDetail = receiptDetailList.stream().filter(x -> x.getMaterialCode().equals(data.getMaterialCode())).findFirst().orElse(null);
            if (receiptDetail == null) {
                throw new ServiceException(StringUtils.format("入库失败,入库单:{} 明细中不存在物料:{}", receiptHeader.getCode(), data.getMaterialCode()));
            }
            receiptContainerDetail.setReceiptId(receiptDetail.getReceiptId());
            receiptContainerDetail.setReceiptDetailId(receiptDetail.getId());
            receiptContainerDetail.setReceiptCode(receiptDetail.getReceiptCode());
            receiptContainerDetail.setSupplierCode(data.getSupplierCode());
            receiptContainerDetail.setSupplierBatch(data.getSupplierBatch());
            receiptContainerDetail.setSupplierTracingNo(data.getSupplierTracingNo());
            receiptContainerDetail.setFirstFlag(data.getFirstFlag());
            receiptContainerDetail.setFlowCode(data.getFlowCode());
            receiptContainerDetail.setLocationNoX(data.getLocationNoX());
            receiptContainerDetail.setLocationNoY(data.getLocationNoY());
            receiptContainerDetail.setTracingNo(data.getTracingNo());
            receiptContainerDetail.setTaskType(receiptContainerHeader.getTaskType());
            receiptContainerDetail.setContainerCode(receiptContainerHeader.getContainerCode());
            receiptContainerDetail.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
            receiptContainerDetail.setMaterialCode(data.getMaterialCode());
            receiptContainerDetail.setMaterialName(material.getName());
            receiptContainerDetail.setMaterialSpec(material.getSpec());
            receiptContainerDetail.setMaterialUnit(material.getUnit());
            receiptContainerDetail.setDescription(material.getDescription());
            receiptContainerDetail.setWeight(receiptDetail.getWeight());
            receiptContainerDetail.setHigh(material.getHigh());
            receiptContainerDetail.setSolidifyTime(data.getSolidifyTime());
            receiptContainerDetail.setWeight(data.getWeight());
            receiptContainerDetail.setQty(data.getQty());
            receiptContainerDetail.setInventorySts(Boolean.TRUE.equals(data.getQualityStatus()) ? QuantityConstant.INVENTORY_STATUS_OK : QuantityConstant.INVENTORY_STATUS_NG);
            receiptContainerDetail.setCreatedBy(QuantityConstant.PLATFORM_MES);
            receiptContainerDetail.setLastUpdatedBy(QuantityConstant.PLATFORM_MES);
            detailList.add(receiptContainerDetail);
        }
        if (!receiptContainerDetailService.saveBatch(detailList)) {
            throw new ServiceException("入库失败,保存入库组盘详情失败");
        }
        //生成入库任务
        List<Integer> idList = Arrays.asList(Convert.toIntArray(String.valueOf(receiptContainerHeader.getId())));
        return receiptTaskService.createReceiptTask(idList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult shipment(MesShipment mesShipment) {
        ShipmentCombinationModel model = new ShipmentCombinationModel();
        model.setTaskNo(mesShipment.getTaskNo());
        model.setOrderCode(mesShipment.getOrderCode());
        model.setToPort(mesShipment.getToPort());
        model.setShipmentMaterialDataList(mesShipment.getShipmentMaterialDataList());
        List<ShipmentContainerHeader> containerHeaderList = shipmentContainerHeaderService.combination(model);
        if (StringUtils.isEmpty(containerHeaderList)) {
            throw new ServiceException("出库失败,出库配盘失败");
        }
        List<Integer> shipmentContainerHeaderIdList = containerHeaderList.stream()
                .map(ShipmentContainerHeader::getId)
                .collect(Collectors.toList());
        return shipmentContainerHeaderService.createTask(shipmentContainerHeaderIdList, mesShipment.getSequence(), mesShipment.getSequenceCount(), mesShipment.getSequenceGroup());
    }

    @Override
    public AjaxResult searchInventory(MesSearch mesSearch) {
        // 查询物料信息
        String orderCode = mesSearch.getOrderCode();
        List<MaterialDataQuery> mesSearchMaterialDataList = new ArrayList<>();
        List<MaterialDataQuery> materialDataList = mesSearch.getMaterialDataList();
        if (materialDataList != null && !materialDataList.isEmpty()) {
            for (MaterialDataQuery materialData : materialDataList) {
                // 计算固化时间
                LambdaQueryWrapper<InventoryDetail> inventoryDetailQueryWrapper = Wrappers.lambdaQuery();
                inventoryDetailQueryWrapper
                        .eq(InventoryDetail::getMaterialCode, materialData.getMaterialCode())
                        .eq(StringUtils.isNotEmpty(materialData.getFlowCode()), InventoryDetail::getFlowCode, materialData.getFlowCode())
                        .eq(!Objects.isNull(materialData.getMaterialInspectStatus()), InventoryDetail::getMaterialInspectStatus, materialData.getMaterialInspectStatus())
                        .eq(StringUtils.isNotEmpty(orderCode), InventoryDetail::getOrderCode, orderCode)
                        .apply("taskQty != qty AND TIMESTAMPDIFF(HOUR, created, now()) >= solidifyTime");
                // 获取某物料的全部库存
                List<InventoryDetail> inventoryDetailList = inventoryDetailService.list(inventoryDetailQueryWrapper);
                BigDecimal allInventoryQty = BigDecimal.ZERO;
                if (StringUtils.isNotEmpty(inventoryDetailList)) {
                    allInventoryQty = inventoryDetailList.stream()
                            .map(InventoryDetail::getQty)
                            .reduce(BigDecimal.ZERO, BigDecimal::add);
                }
                materialData.setUsableQty(allInventoryQty);
                materialData.setMaterialCode(materialData.getMaterialCode());
                mesSearchMaterialDataList.add(materialData);
            }
        }
        mesSearch.setMaterialDataList(mesSearchMaterialDataList);

        // 查询空盛具
        List<VehicleDataQuery> vehicleDataQueryList = mesSearch.getVehicleDataList();
        if (vehicleDataQueryList != null && !vehicleDataQueryList.isEmpty()) {
            List<VehicleDataQuery> mesSearchVehicleDataList = new ArrayList<>();
            vehicleDataQueryList.forEach(vehicleType -> {
                List<Container> containerList = containerService.getEmptyByVehicleType(vehicleType.getCode());
                VehicleDataQuery mesSearchVehicleData = new VehicleDataQuery();
                mesSearchVehicleData.setCode(vehicleType.getCode());
                mesSearchVehicleData.setUsableQty(containerList.size());
                mesSearchVehicleDataList.add(mesSearchVehicleData);
            });
            mesSearch.setVehicleDataList(mesSearchVehicleDataList);
        }
        return AjaxResult.success(mesSearch);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult workOrder(MesWorkOrder mesWorkOrder) {
        String taskNo = mesWorkOrder.getTaskNo();
        if (StringUtils.isEmpty(taskNo)) {
            return AjaxResult.error("下发工单失败,任务号为空");
        }
        String orderCode = mesWorkOrder.getOrderCode();
        if (StringUtils.isEmpty(orderCode)) {
            return AjaxResult.error("下发工单失败,工单号为空");
        }
        String batchNumber = mesWorkOrder.getBatchNumber();
        if (StringUtils.isEmpty(batchNumber)) {
            return AjaxResult.error("生产批号为空");
        }
        String orderNumber = mesWorkOrder.getOrderNumber();
        if (StringUtils.isEmpty(orderNumber)) {
            return AjaxResult.error("生产令号为空");
        }
        Integer orderStatus = mesWorkOrder.getOrderStatus();
        if (orderStatus == null) {
            return AjaxResult.error("下发工单失败,工单状态为空");
        }
        WorkOrderHeader workOrderHeader = workOrderHeaderService.getOrderByCode(orderCode);
        if (workOrderHeader == null) {
            workOrderHeader = new WorkOrderHeader();
        } else {
            return AjaxResult.error(StringUtils.format("下发工单失败,工单:{} 已存在", orderCode));
            /*WorkOrderHeader updateWorkOrderHeader = new WorkOrderHeader();
            updateWorkOrderHeader.setId(workOrderHeader.getId());
            updateWorkOrderHeader.setOrderStatus(orderStatus);
            boolean success = workOrderHeaderService.updateById(updateWorkOrderHeader);
            if (success) {
                return AjaxResult.success("更新工单状态成功");
            } else {
                return AjaxResult.error(StringUtils.format("更新工单状态失败,工单号:{}", orderCode));
            }*/
        }
        workOrderHeader.setTaskNo(taskNo);
        workOrderHeader.setOrderCode(orderCode);
        workOrderHeader.setOrderNumber(orderNumber);
        workOrderHeader.setBatchNumber(batchNumber);
        workOrderHeader.setOrderStatus(orderStatus);
        if (!workOrderHeaderService.save(workOrderHeader)) {
            throw new ServiceException("下发工单失败,保存工单头表失败");
        }
        List<WorkOrderDetail> workOrderDetailList = new ArrayList<>();
        for (MesWorkOrderDetail mesWorkOrderDetail : mesWorkOrder.getMaterialDataList()) {
            WorkOrderDetail workOrderDetail = new WorkOrderDetail();
            workOrderDetail.setWorkId(workOrderHeader.getId());
            Material material = materialService.getMaterialByCode(mesWorkOrderDetail.getMaterialCode(), QuantityConstant.DEFAULT_WAREHOUSE);
            if (material == null) {
                throw new ServiceException(StringUtils.format("根据物料编码:{} 没有找到对应物料信息", mesWorkOrderDetail.getMaterialCode()));
            }
            workOrderDetail.setMaterialCode(mesWorkOrderDetail.getMaterialCode());
            workOrderDetail.setMaterialQty(mesWorkOrderDetail.getMaterialQty());
            workOrderDetail.setShipmentReferQty(mesWorkOrderDetail.getShipmentReferQty());
            workOrderDetailList.add(workOrderDetail);
        }
        boolean success = workOrderDetailService.saveBatch(workOrderDetailList);
        if (!success) {
            throw new ServiceException("下发工单失败,保存工单明细失败");
        }
        return AjaxResult.success("下发工单成功");
    }

    @Override
    public AjaxResult backShipment(Integer taskHeaderId) {
        TaskHeader taskHeader = taskHeaderService.getById(taskHeaderId);
        if (StringUtils.isNull(taskHeader)) {
            return AjaxResult.error(StringUtils.format("回传出库信息失败,没有找到任务,id:{}", taskHeaderId));
        }

        // 获取任务明细
        LambdaQueryWrapper<TaskDetail> taskDetailQueryWrapper = Wrappers.lambdaQuery();
        taskDetailQueryWrapper.eq(TaskDetail::getTaskId, taskHeaderId);
        List<TaskDetail> taskDetailList = taskDetailService.list(taskDetailQueryWrapper);
        TaskDetail taskDetailQuery = taskDetailList.stream().findAny().orElse(null);
        if (taskDetailQuery == null) {
            return AjaxResult.error(StringUtils.format("回传出库信息失败,任务:{} 没有明细", taskHeaderId));
        }
        MesReturn mesReturn = new MesReturn();
        List<MaterialData> materialDataList = new ArrayList<>();
        taskDetailList.forEach(taskDetail -> {
            MaterialData materialData = new MaterialData();
            materialData.setIsMaterial(true);
            materialData.setMaterialCode(taskDetail.getMaterialCode());
            materialData.setQty(taskDetail.getQty());
            materialData.setTracingNo(taskDetail.getTracingNo());
            materialData.setQualityStatus(QuantityConstant.INVENTORY_STATUS_OK.equals(taskDetail.getInventorySts()));
            materialData.setSupplierCode(taskDetail.getSupplierCode());
            materialData.setSupplierTracingNo(taskDetail.getSupplierTracingNo());
            materialData.setLocationNoX(taskDetail.getLocationNoX());
            materialData.setLocationNoY(taskDetail.getLocationNoY());
            materialData.setFlowCode(taskDetail.getFlowCode());
            materialData.setFirstFlag(taskDetail.getFirstFlag());
            materialData.setWeight(taskDetail.getWeight());
            materialData.setSolidifyTime(taskDetail.getRealSolidifyTime());
            materialDataList.add(materialData);
        });
        Container container = containerService.getContainerByCode(taskHeader.getContainerCode(), QuantityConstant.DEFAULT_WAREHOUSE);
        if (container == null) {
            throw new ServiceException(StringUtils.format("出库回传失败,不存在载具编码:{}", taskHeader.getContainerCode()));
        }
        Vehicle vehicle = vehicleService.getVehicleByCode(taskHeader.getVehicleCode());
        if (Objects.isNull(vehicle)) {
            throw new ServiceException(StringUtils.format("出库回传失败,不存在盛具编码: {}", taskHeader.getVehicleCode()));
        }
        List<String> collect1 = new ArrayList<>();
        VehicleType vehicleType = vehicleTypeService.getByCode(vehicle.getVehicleType());
        for (int y = 1; y <= vehicleType.getMaxX(); y++) {
            for (int x = 1; x <= vehicleType.getMaxY(); x++) {
                collect1.add(x + "_" + y);
            }
        }
        String materialCode = materialDataList.stream().map(MaterialData::getMaterialCode).findFirst().orElse(null);
        List<String> collect2 = materialDataList.stream()
                .map(item -> item.getLocationNoX() + "_" + item.getLocationNoY())
                .collect(Collectors.toList());
        // 没物料的坐标
        List<String> collect = collect1.stream().filter(item -> !collect2.contains(item)).collect(Collectors.toList());
        collect.forEach(item -> {
            String[] split = item.split("_");
            MaterialData newMaterialData = new MaterialData();
            newMaterialData.setMaterialCode(materialCode);
            newMaterialData.setTracingNo("");
            newMaterialData.setQty(BigDecimal.ONE);
            newMaterialData.setSupplierCode("");
            newMaterialData.setSupplierTracingNo("");
            newMaterialData.setQualityStatus(true);
            newMaterialData.setFirstFlag(false);
            newMaterialData.setFlowCode("");
            newMaterialData.setLocationNoX(Integer.parseInt(split[0]));
            newMaterialData.setLocationNoY(Integer.parseInt(split[1]));
            newMaterialData.setIsMaterial(false);
            newMaterialData.setWeight(null);
            newMaterialData.setSolidifyTime(null);
            materialDataList.add(newMaterialData);
        });

        mesReturn.setTaskNo(taskHeader.getTaskNo());
        mesReturn.setTaskStatus(QuantityConstant.MES_TASK_COMPLETE);
        mesReturn.setOrderCode(taskHeader.getOrderCode());
        mesReturn.setToPort(taskHeader.getPort());
        // 因和MES那边载具和盛具字段定义不一致,所以要反转再回传
        mesReturn.setContainerTypeCode(vehicleType.getCode());
        mesReturn.setContainerCode(taskHeader.getVehicleCode());
        mesReturn.setVehicleCode(container.getCode());
        mesReturn.setVehicleTypeCode(container.getContainerType());
        mesReturn.setMaxX(vehicleType.getMaxX());
        mesReturn.setMaxY(vehicleType.getMaxY());
        mesReturn.setMaterialDataList(materialDataList);
        String url = addressService.selectAddress(QuantityConstant.ADDRESS_MES, QuantityConstant.DEFAULT_WAREHOUSE, QuantityConstant.DEFAULT_AREA);
        String jsonParam = JSON.toJSONString(mesReturn);
        ResponseEntity<JSONObject> result = RestUtil.request_post(url, QuantityConstant.DEFAULT_WAREHOUSE, jsonParam);
        if (result != null && result.getBody() != null) {
            String code = result.getBody().getString("code");
            String msg = result.getBody().getString("msg");
            if (!HttpConstant.isSuccess(Integer.parseInt(code))) {
                return AjaxResult.error(msg);
            }
        } else {
            throw new ServiceException("出库回传失败,接口地址错误或返回为空");
        }

        taskHeader.setIsBack(QuantityConstant.MES_TASK_BACK);
        if (!taskHeaderService.updateById(taskHeader)) {
            throw new ServiceException("出库回传失败,更新任务头表失败");
        }
        LambdaQueryWrapper<ShipmentHeader> shipmentHeaderQueryWrapper = Wrappers.lambdaQuery();
        shipmentHeaderQueryWrapper.eq(ShipmentHeader::getCode, taskHeader.getShipmentCode());
        ShipmentHeader shipmentHeader = shipmentHeaderService.getOne(shipmentHeaderQueryWrapper);
        if (QuantityConstant.SHIPMENT_HEADER_COMPLETED.equals(shipmentHeader.getFirstStatus())
                && QuantityConstant.SHIPMENT_HEADER_COMPLETED.equals(shipmentHeader.getLastStatus())) {
            shipmentHeader.setFirstStatus(QuantityConstant.SHIPMENT_HEADER_RETURN);
            shipmentHeader.setLastStatus(QuantityConstant.SHIPMENT_HEADER_RETURN);
            if (!shipmentHeaderService.updateById(shipmentHeader)) {
                throw new ServiceException("出库回传失败,更新出库单头表失败");
            }
        }
        return AjaxResult.success(mesReturn);
    }

    @Override
    public AjaxResult backReceipt(Integer taskHeaderId) {
        TaskHeader taskHeader = taskHeaderService.getById(taskHeaderId);
        if (taskHeader == null) {
            return AjaxResult.error("回传MES入库信息失败,任务头表为空");
        }

        MesReturn mesReturn = new MesReturn();
        // 获取任务明细
        LambdaQueryWrapper<TaskDetail> taskDetailQueryWrapper = Wrappers.lambdaQuery();
        taskDetailQueryWrapper.eq(TaskDetail::getTaskId, taskHeaderId);
        List<TaskDetail> taskDetailList = taskDetailService.list(taskDetailQueryWrapper);

        List<MaterialData> materialDataList = new ArrayList<>();
        taskDetailList.forEach(taskDetail -> {
            MaterialData materialData = new MaterialData();
            materialData.setMaterialCode(taskDetail.getMaterialCode());
            materialData.setQty(taskDetail.getQty());
            materialData.setTracingNo(taskDetail.getTracingNo());
            materialData.setSupplierCode(taskDetail.getSupplierCode());
            materialData.setSupplierTracingNo(taskDetail.getSupplierTracingNo());
            materialData.setQualityStatus(QuantityConstant.INVENTORY_STATUS_OK.equals(taskDetail.getInventorySts()));
            materialData.setLocationNoX(taskDetail.getLocationNoX());
            materialData.setLocationNoY(taskDetail.getLocationNoY());
            materialData.setFlowCode(taskDetail.getFlowCode());
            materialData.setFirstFlag(taskDetail.getFirstFlag());
            materialData.setIsMaterial(true);
            materialDataList.add(materialData);
        });
        Container container = containerService.getContainerByCode(taskHeader.getContainerCode(), QuantityConstant.DEFAULT_WAREHOUSE);
        Vehicle vehicle = vehicleService.getVehicleByCode(taskHeader.getVehicleCode());
        if (Objects.isNull(vehicle)) {
            throw new ServiceException(StringUtils.format("出库回传失败,不存在盛具编码: {}", taskHeader.getVehicleCode()));
        }
        mesReturn.setTaskNo(taskHeader.getTaskNo());
        mesReturn.setTaskStatus(QuantityConstant.MES_TASK_COMPLETE);
        mesReturn.setOrderCode(taskHeader.getOrderCode());
        mesReturn.setToPort(taskHeader.getFromPort());
        // 因和MES那边载具和盛具字段定义不一致,所以要反转再回传
        mesReturn.setContainerTypeCode(vehicle.getVehicleType());
        mesReturn.setContainerCode(taskHeader.getVehicleCode());
        mesReturn.setVehicleCode(taskHeader.getContainerCode());
        mesReturn.setVehicleTypeCode(container.getContainerType());
        mesReturn.setMaterialDataList(materialDataList);
        mesReturn.setMaterialCount(materialDataList.stream().map(MaterialData::getQty).reduce(BigDecimal.ZERO, BigDecimal::add));
        String url = addressService.selectAddress(QuantityConstant.ADDRESS_MES, QuantityConstant.DEFAULT_WAREHOUSE, QuantityConstant.DEFAULT_AREA);
        String jsonParam = JSON.toJSONString(mesReturn);
        ResponseEntity<JSONObject> result = RestUtil.request_post(url, QuantityConstant.DEFAULT_WAREHOUSE, jsonParam);
        if (result != null && result.getBody() != null) {
            String code = result.getBody().getString("code");
            String msg = result.getBody().getString("msg");
            if (!HttpConstant.isSuccess(Integer.parseInt(code))) {
                return AjaxResult.error(msg);
            }
        } else {
            throw new ServiceException("接口地址错误或返回为空");
        }

        TaskHeader updateTaskHeader = new TaskHeader();
        updateTaskHeader.setId(taskHeader.getId());
        updateTaskHeader.setIsBack(QuantityConstant.MES_TASK_BACK);
        if (!taskHeaderService.updateById(updateTaskHeader)) {
            throw new ServiceException("入库回传失败,更新任务头表失败");
        }
        // 一个任务一个入库单 所以可以直接修改库存头状态
        LambdaQueryWrapper<ReceiptHeader> receiptHeaderQueryWrapper = Wrappers.lambdaQuery();
        ReceiptHeader receiptHeader = receiptHeaderService.getOne(receiptHeaderQueryWrapper);
        if (QuantityConstant.RECEIPT_HEADER_POSTING.equals(receiptHeader.getFirstStatus())
                && QuantityConstant.RECEIPT_HEADER_POSTING.equals(receiptHeader.getLastStatus())) {
            receiptHeader.setFirstStatus(QuantityConstant.RECEIPT_HEADER_RETURN);
            receiptHeader.setLastStatus(QuantityConstant.RECEIPT_HEADER_RETURN);
            if (!receiptHeaderService.updateById(receiptHeader)) {
                throw new ServiceException("出库回传失败,更新入库单头表失败");
            }
        }
        return AjaxResult.success(mesReturn);
    }

    @Override
    public AjaxResult backEmpty(Integer id) {
        if (Objects.isNull(id)) {
            return AjaxResult.error("任务id不能为空");
        }
        TaskHeader taskHeader = taskHeaderService.getById(id);
        Container container = containerService.getContainerByCode(taskHeader.getContainerCode(), QuantityConstant.DEFAULT_WAREHOUSE);
        if (Objects.isNull(container)) {
            throw new ServiceException("回传空载具入出库时,没有找到载具: ".concat(taskHeader.getContainerCode()));
        }
        Vehicle vehicle = vehicleService.getVehicleByCode(taskHeader.getVehicleCode(), QuantityConstant.DEFAULT_WAREHOUSE);
        if (Objects.isNull(vehicle)) {
            throw new ServiceException("回传空载具入出库时,没有找到盛具: ".concat(taskHeader.getVehicleCode()));
        }
        VehicleType vehicleType = vehicleTypeService.getByCode(vehicle.getVehicleType());
        if (Objects.isNull(vehicleType)) {
            throw new ServiceException("回传空载具入出库时,没有找到盛具类型: ".concat(vehicle.getVehicleType()));
        }
        MesReturn mesReturn = new MesReturn();
        mesReturn.setTaskNo(taskHeader.getTaskNo());
        mesReturn.setOrderCode(taskHeader.getOrderCode());
        mesReturn.setTaskStatus(QuantityConstant.MES_TASK_COMPLETE);
        if (QuantityConstant.TASK_TYPE_EMPTYRECEIPT == taskHeader.getTaskType()) {
            mesReturn.setToPort(taskHeader.getFromPort());
        }
        if (QuantityConstant.TASK_TYPE_EMPTYSHIPMENT == taskHeader.getTaskType()) {
            mesReturn.setToPort(taskHeader.getPort());
        }
        // 因和MES那边载具和盛具字段定义不一致,所以要反转再回传
        mesReturn.setContainerTypeCode(vehicle.getVehicleType());
        mesReturn.setContainerCode(vehicle.getCode());
        mesReturn.setVehicleCode(container.getCode());
        mesReturn.setVehicleTypeCode(container.getContainerType());

        Integer maxX = vehicleType.getMaxX();
        Integer maxY = vehicleType.getMaxY();
        mesReturn.setMaxX(maxX);
        mesReturn.setMaxY(maxY);
        // MES方需求,空盘也要给坐标
        List<MaterialData> materialDataList = new ArrayList<>();
        for (int x = 1; x <= maxX; x++) {
            for (int y = 1; y <= maxY; y++) {
                MaterialData materialData = new MaterialData();
                materialData.setMaterialCode("");
                materialData.setTracingNo("");
                materialData.setQty(BigDecimal.ONE);
                materialData.setSupplierCode("");
                materialData.setSupplierTracingNo("");
                materialData.setQualityStatus(true);
                materialData.setFirstFlag(false);
                materialData.setFlowCode("");
                materialData.setLocationNoX(x);
                materialData.setLocationNoY(y);
                materialData.setIsMaterial(false);
                materialDataList.add(materialData);
            }
        }
        mesReturn.setMaterialDataList(materialDataList);
        String url = addressService.selectAddress(QuantityConstant.ADDRESS_MES, QuantityConstant.DEFAULT_WAREHOUSE, QuantityConstant.DEFAULT_AREA);
        String jsonParam = JSON.toJSONString(mesReturn);
        ResponseEntity<JSONObject> result = RestUtil.request_post(url, QuantityConstant.DEFAULT_WAREHOUSE, jsonParam);
        if (result != null && result.getBody() != null) {
            String code = result.getBody().getString("code");
            String msg = result.getBody().getString("msg");
            if (!HttpConstant.isSuccess(Integer.parseInt(code))) {
                return AjaxResult.error(msg);
            }
        } else {
            throw new ServiceException("接口地址错误或返回为空");
        }
        taskHeader.setIsBack(QuantityConstant.MES_TASK_BACK);
        taskHeaderService.updateById(taskHeader);
        return AjaxResult.success(mesReturn);
    }

    @Override
    public AjaxResult backChangeStation(Integer taskId) {
        if (Objects.isNull(taskId)) {
            return AjaxResult.error("任务id不能为空");
        }
        TaskHeader taskHeader = taskHeaderService.getById(taskId);
        Container container = containerService.getContainerByCode(taskHeader.getContainerCode(), QuantityConstant.DEFAULT_WAREHOUSE);
        if (Objects.isNull(container)) {
            throw new ServiceException("换站任务,没有找到载具: ".concat(taskHeader.getContainerCode()));
        }
        Vehicle vehicle = vehicleService.getByCode(taskHeader.getVehicleCode());
        if (Objects.isNull(vehicle)) {
            throw new ServiceException("换站任务,没有找到盛具具: ".concat(taskHeader.getVehicleCode()));
        }
        VehicleType vehicleType = vehicleTypeService.getByCode(vehicle.getVehicleType());
        if (vehicleType == null) {
            throw new ServiceException(StringUtils.format("换站任务回传失败,盛具类型:{} 未找到,任务id:{}: ", vehicle.getVehicleType(), taskId));
        }
        MesReturn mesReturn = new MesReturn();
        mesReturn.setTaskNo(taskHeader.getTaskNo());
        mesReturn.setTaskStatus(QuantityConstant.MES_TASK_COMPLETE);
        mesReturn.setToPort(taskHeader.getPort());
        // 因和MES那边载具和盛具字段定义不一致,所以要反转再回传
        mesReturn.setContainerTypeCode(vehicle.getVehicleType());
        mesReturn.setContainerCode(vehicle.getCode());
        mesReturn.setVehicleCode(container.getCode());
        mesReturn.setVehicleTypeCode(container.getContainerType());
        mesReturn.setMaxX(vehicleType.getMaxX());
        mesReturn.setMaxY(vehicleType.getMaxY());
        mesReturn.setOrderCode(taskHeader.getOrderCode());

        //回传物料信息
        List<MaterialData> materialDataList = new ArrayList<>();
        List<TaskDetail> taskDetailList = taskDetailService.getByHeaderId(taskHeader.getId());
        taskDetailList.forEach(taskDetail -> {
            MaterialData materialData = new MaterialData();
            materialData.setIsMaterial(true);
            materialData.setMaterialCode(taskDetail.getMaterialCode());
            materialData.setQty(taskDetail.getQty());
            materialData.setTracingNo(taskDetail.getTracingNo());
            materialData.setQualityStatus(QuantityConstant.INVENTORY_STATUS_OK.equals(taskDetail.getInventorySts()));
            materialData.setSupplierCode(taskDetail.getSupplierCode());
            materialData.setSupplierTracingNo(taskDetail.getSupplierTracingNo());
            materialData.setLocationNoX(taskDetail.getLocationNoX());
            materialData.setLocationNoY(taskDetail.getLocationNoY());
            materialData.setFlowCode(taskDetail.getFlowCode());
            materialData.setFirstFlag(taskDetail.getFirstFlag());
            materialData.setWeight(taskDetail.getWeight());
            materialData.setSolidifyTime(taskDetail.getSolidifyTime());
            materialDataList.add(materialData);
        });

        String materialCode = materialDataList.stream().map(MaterialData::getMaterialCode).findFirst().orElse(null);
        List<String> collect1 = new ArrayList<>();
        for (int y = 1; y < vehicleType.getMaxX(); y++) {
            for (int x = 1; x < vehicleType.getMaxY(); x++) {
                collect1.add(x + "_" + y);
            }
        }
        List<String> collect2 = materialDataList.stream()
                .map(item -> item.getLocationNoX() + "_" + item.getLocationNoY())
                .collect(Collectors.toList());
        List<String> collect = collect1.stream().filter(item -> !collect2.contains(item)).collect(Collectors.toList());

        // 没物料的坐标
        collect.forEach(item -> {
            String[] split = item.split("_");
            MaterialData newMaterialData = new MaterialData();
            newMaterialData.setMaterialCode(materialCode);
            newMaterialData.setTracingNo("");
            newMaterialData.setQty(BigDecimal.ONE);
            newMaterialData.setSupplierCode("");
            newMaterialData.setSupplierTracingNo("");
            newMaterialData.setQualityStatus(true);
            newMaterialData.setFirstFlag(false);
            newMaterialData.setFlowCode("");
            newMaterialData.setLocationNoX(Integer.parseInt(split[0]));
            newMaterialData.setLocationNoY(Integer.parseInt(split[1]));
            newMaterialData.setIsMaterial(false);
            newMaterialData.setWeight(null);
            newMaterialData.setSolidifyTime(null);
            materialDataList.add(newMaterialData);
        });

        mesReturn.setMaterialDataList(materialDataList);

        String url = addressService.selectAddress(QuantityConstant.ADDRESS_MES, QuantityConstant.DEFAULT_WAREHOUSE, QuantityConstant.DEFAULT_AREA);
        String jsonParam = JSON.toJSONString(mesReturn);
        ResponseEntity<JSONObject> result = RestUtil.request_post(url, QuantityConstant.DEFAULT_WAREHOUSE, jsonParam);
        if (result != null && result.getBody() != null) {
            String code = result.getBody().getString("code");
            String msg = result.getBody().getString("msg");
            if (!HttpConstant.isSuccess(Integer.parseInt(code))) {
                return AjaxResult.error(msg);
            }
        } else {
            throw new ServiceException("接口地址错误或返回为空");
        }
        taskHeader.setIsBack(QuantityConstant.MES_TASK_BACK);
        taskHeaderService.updateById(taskHeader);
        return AjaxResult.success(mesReturn);
    }

    @Override
    public AjaxResult searchInventoryByQC(MesSearchRequestByQCDto mesSearchRequestByQCDto) {
        // 参数校验
        String taskNo = mesSearchRequestByQCDto.getTaskNo();
        String orderCode = mesSearchRequestByQCDto.getOrderCode();
        String materialCode = mesSearchRequestByQCDto.getMaterialCode();
        if (StringUtils.isEmpty(taskNo)) {
            throw new ServiceException("任务号为空");
        }
        if (StringUtils.isEmpty(orderCode)) {
            throw new ServiceException("工单号为空");
        }
        if (StringUtils.isEmpty(materialCode)) {
            throw new ServiceException("物料编码为空");
        }
        Material material = materialService.getByCode(materialCode);
        if (material == null) {
            return AjaxResult.error(StringUtils.format("物料编码:{} 不存在", materialCode));
        }
        // 根据工单号和物料编码查询库存,排除被领料单冻结的和已存在任务数量的
        LambdaQueryWrapper<InventoryDetail> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(InventoryDetail::getMaterialCode, materialCode)
                .eq(InventoryDetail::getOrderCode, orderCode)
                .ne(InventoryDetail::getZoneCode, QuantityConstant.ZONE_TYPE_CBG)
                .last("and taskQty != qty");
        List<InventoryDetail> inventoryDetailList = inventoryDetailService.list(queryWrapper);
        List<MaterialDataQueryByQCDto> materialDataQueryByQCDtoList = new LinkedList<>();
        inventoryDetailList.forEach(inventoryDetail -> {
            MaterialDataQueryByQCDto queryByQCDto = new MaterialDataQueryByQCDto();
            queryByQCDto.setLocationNoX(inventoryDetail.getLocationNoX());
            queryByQCDto.setLocationNoY(inventoryDetail.getLocationNoY());
            queryByQCDto.setTracingNo(inventoryDetail.getTracingNo());
            queryByQCDto.setQualityStatus(Objects.equals(inventoryDetail.getInventorySts(), QuantityConstant.INVENTORY_STATUS_OK));
            queryByQCDto.setContainerCode(inventoryDetail.getContainerCode());
            queryByQCDto.setContainerTypeCode(inventoryDetail.getContainerCode().substring(0, 5));//ZJ-01-00001 -> ZJ-01
            queryByQCDto.setVehicleCode(inventoryDetail.getVehicleCode());
            queryByQCDto.setVehicleTypeCode(inventoryDetail.getVehicleCode().substring(0, 7));//CJ00001-00001 -> CJ00001
            queryByQCDto.setLocationCode(inventoryDetail.getLocationCode());
            materialDataQueryByQCDtoList.add(queryByQCDto);
        });
        MesSearchResponseByQCDto mesSearchResponseByQCDto = new MesSearchResponseByQCDto();
        mesSearchResponseByQCDto.setMaterialCode(materialCode);
        mesSearchResponseByQCDto.setMaterialName(material.getName());
        mesSearchResponseByQCDto.setUsableQty(inventoryDetailList.stream().map(InventoryDetail::getQty).reduce(BigDecimal.ZERO, BigDecimal::add));
        mesSearchResponseByQCDto.setMaterialDataQueryByQCDtoList(materialDataQueryByQCDtoList);
        return AjaxResult.success(mesSearchResponseByQCDto);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult shipmentByQC(MesShipmentByQCDto mesShipmentByQCDto) {
        String taskNo = mesShipmentByQCDto.getTaskNo();
        String vehicleCode = mesShipmentByQCDto.getVehicleCode();
        Integer materialInspectStatus = mesShipmentByQCDto.getMaterialInspectStatus();
        String toPort = mesShipmentByQCDto.getToPort();
        // 1、参数校验
        if (StringUtils.isEmpty(toPort)) {
            return AjaxResult.error("抽样质检出库失败,目标料点为空");
        }
        if (StringUtils.isEmpty(vehicleCode)) {
            return AjaxResult.error("抽样质检出库失败,盛具编码为空");
        }
        if (Objects.isNull(materialInspectStatus)) {
            return AjaxResult.error("抽样质检出库失败,物料检验状态为空");
        }
        // 2、获取库存信息
        LambdaQueryWrapper<InventoryDetail> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper
                .apply("qty > taskQty")
                .eq(InventoryDetail::getVehicleCode, vehicleCode)
                .eq(InventoryDetail::getMaterialInspectStatus, materialInspectStatus);
        List<InventoryDetail> inventoryDetailList = inventoryDetailService.list(queryWrapper);
        if (inventoryDetailList.isEmpty()) {
            return AjaxResult.error(StringUtils.format("抽样质检出库失败,根据盛具:{},检验状态:{} 没有找到库存信息",
                    vehicleCode, mesShipmentByQCDto.getMaterialInspectStatus()));
        }
        String shipmentType;
        InventoryDetail tempInventoryDetail = inventoryDetailList.stream().findAny().orElseThrow(() -> new ServiceException("库存数据错误"));
        switch (tempInventoryDetail.getMaterialInspectStatus()) {
            case 1:
                shipmentType = QuantityConstant.SHIPMENT_TYPE_QC_SELF;
                break;
            case 3:
                shipmentType = QuantityConstant.SHIPMENT_TYPE_QC_OFFICE;
                break;
            case 4:
                shipmentType = QuantityConstant.SHIPMENT_TYPE_QC_ARMY;
                break;
            default:
                return AjaxResult.error("抽样质检出库失败,库存物料状态错误");
        }

        InventoryHeader inventoryHeader = inventoryHeaderService.getById(tempInventoryDetail.getInventoryHeaderId());
        if (inventoryHeader == null) {
            return AjaxResult.error(StringUtils.format("抽样质检出库失败,库存头信息为空"));
        }
        Container container = containerService.getByCode(inventoryHeader.getContainerCode());
        if (container == null) {
            return AjaxResult.error(StringUtils.format("抽样质检出库失败,根据库存头载具:{} 未找到载具", inventoryHeader.getContainerCode()));
        }
        String containerCode = container.getCode();
        if (QuantityConstant.STATUS_CONTAINER_LOCK.equals(container.getStatus())) {
            return AjaxResult.error(StringUtils.format("抽样质检出库失败,载具:{} 已锁定", containerCode));
        }
        String locationCode = container.getLocationCode();
        if (StringUtils.isEmpty(locationCode)) {
            return AjaxResult.error(StringUtils.format("抽样质检出库失败,载具:{} 不在库位上", containerCode));
        }
        Location location = locationService.getLocationByCode(locationCode);
        if (location == null) {
            throw new ServiceException(StringUtils.format("抽样质检出库失败,库位:{} 不存在", locationCode));
        }
        if (QuantityConstant.STATUS_LOCATION_LOCK.equals(location.getStatus())) {
            throw new ServiceException(StringUtils.format("抽样质检出库失败,库位:{} 已被锁定", location.getCode()));
        }
        // 3、创建出库单
        String code = shipmentHeaderService.createCode(shipmentType);
        ShipmentHeader shipmentHeader = new ShipmentHeader();
        shipmentHeader.setWarehouseCode(QuantityConstant.DEFAULT_WAREHOUSE);
        shipmentHeader.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
        shipmentHeader.setCode(code);
        shipmentHeader.setShipmentType(shipmentType);
        shipmentHeader.setFirstStatus(QuantityConstant.SHIPMENT_HEADER_GROUPDISK);
        shipmentHeader.setLastStatus(QuantityConstant.SHIPMENT_HEADER_GROUPDISK);
        shipmentHeader.setCreatedBy(QuantityConstant.PLATFORM_MES);
        shipmentHeader.setLastUpdatedBy(QuantityConstant.PLATFORM_MES);
        boolean success = shipmentHeaderService.save(shipmentHeader);
        if (!success) {
            throw new ServiceException("抽样质检出库失败,保存出库单头表失败");
        }

        ShipmentDetail shipmentDetail = new ShipmentDetail();
        BeanUtils.copyBeanProp(shipmentDetail, tempInventoryDetail);
        shipmentDetail.setShipmentId(shipmentHeader.getId());
        shipmentDetail.setShipmentCode(shipmentHeader.getCode());
        shipmentDetail.setStatus(shipmentHeader.getFirstStatus());
        shipmentDetail.setQty(inventoryDetailList.stream().map(InventoryDetail::getQty).reduce(BigDecimal.ZERO, BigDecimal::add));
        shipmentDetail.setTaskQty(shipmentDetail.getQty());
        shipmentDetail.setCreatedBy(shipmentHeader.getCreatedBy());
        shipmentDetail.setCreated(new Date());
        shipmentDetail.setLastUpdatedBy(shipmentHeader.getLastUpdatedBy());
        shipmentDetail.setLastUpdated(new Date());
        shipmentDetail.setStatus(shipmentHeader.getFirstStatus());

        success = shipmentDetailService.save(shipmentDetail);
        if (!success) {
            throw new ServiceException(StringUtils.format("抽样质检出库失败,保存抽样检验出库单明细失败"));
        }
        // 更新抽样检验出库单头表
        ShipmentHeader updateShipmentHeader = new ShipmentHeader();
        updateShipmentHeader.setId(shipmentHeader.getId());
        updateShipmentHeader.setTotalLines(1);
        updateShipmentHeader.setTotalQty(shipmentDetail.getQty());
        updateShipmentHeader.setLastUpdatedBy(shipmentHeader.getLastUpdatedBy());
        success = shipmentHeaderService.updateById(updateShipmentHeader);
        if (!success) {
            throw new ServiceException(StringUtils.format("抽样质检出库失败,更新抽样检验出库单头表数量失败"));
        }

        // 4、生成配盘记录
        ShipmentContainerHeader shipmentContainerHeader = shipmentContainerHeaderService.getUnCompleteByContainerCode(containerCode);
        if (shipmentContainerHeader == null) {
            shipmentContainerHeader = new ShipmentContainerHeader();
            BeanUtils.copyBeanProp(shipmentContainerHeader, inventoryHeader);
            shipmentContainerHeader.setShipmentCode(shipmentHeader.getCode());
            shipmentContainerHeader.setStatus(QuantityConstant.SHIPMENT_CONTAINER_TASK);
            shipmentContainerHeader.setPort(toPort);
            boolean success2 = shipmentContainerHeaderService.save(shipmentContainerHeader);
            if (!success2) {
                throw new ServiceException("抽样检验出库失败,保存配盘头表失败");
            }
        }
        List<InventoryDetail> inventoryDetails = inventoryDetailList.stream()
                .filter(x -> x.getMaterialCode().equals(shipmentDetail.getMaterialCode()) &&
                        x.getInventorySts().equals(shipmentDetail.getInventorySts()))
                .collect(Collectors.toList());
        if (StringUtils.isEmpty(inventoryDetails)) {
            throw new ServiceException(StringUtils.format("抽样检验出库失败,检验出库单据明细中未找到状态为:{}的物料:{}",
                    shipmentDetail.getInventorySts(), shipmentDetail.getMaterialCode()));
        }
        ShipmentContainerHeader finalShipmentContainerHeader = shipmentContainerHeader;
        inventoryDetails.forEach(inventoryDetail -> {
            // 配盘明细
            ShipmentContainerDetail shipmentContainerDetail = new ShipmentContainerDetail();
            BeanUtils.copyBeanProp(shipmentContainerDetail, inventoryDetail);
            shipmentContainerDetail.setId(null);
            shipmentContainerDetail.setShippingContainerId(finalShipmentContainerHeader.getId());
            shipmentContainerDetail.setInventoryId(inventoryDetail.getId());
            shipmentContainerDetail.setShipmentCode(shipmentHeader.getCode());
            shipmentContainerDetail.setShipmentId(shipmentHeader.getId());
            shipmentContainerDetail.setShipmentDetailId(shipmentDetail.getId());
            shipmentContainerDetail.setStatus(QuantityConstant.SHIPMENT_CONTAINER_TASK);
            shipmentContainerDetail.setCreated(new Date());
            shipmentContainerDetail.setCreatedBy(QuantityConstant.PLATFORM_MES);
            shipmentContainerDetail.setLastUpdated(new Date());
            shipmentContainerDetail.setLastUpdatedBy(QuantityConstant.PLATFORM_MES);
            if (!shipmentContainerDetailService.save(shipmentContainerDetail)) {
                throw new ServiceException("抽样检验出库失败,保存出库配盘明细失败");
            }
        });

        // 5、创建出库任务
        TaskHeader taskHeader = new TaskHeader();
        BeanUtils.copyBeanProp(taskHeader, shipmentContainerHeader);
        taskHeader.setTaskType(QuantityConstant.TASK_TYPE_WHOLESHIPMENT);
        taskHeader.setAllocationHeadId(shipmentContainerHeader.getId());
        taskHeader.setInternalTaskType(QuantityConstant.TASK_INTENERTYPE_SHIPMENT);
        taskHeader.setFromLocation(shipmentContainerHeader.getLocationCode());
        taskHeader.setZoneCode(location.getZoneCode());
        taskHeader.setStatus(QuantityConstant.TASK_STATUS_BUILD);
        taskHeader.setPort(toPort);
        taskHeader.setCreated(new Date());
        taskHeader.setCreatedBy(QuantityConstant.PLATFORM_MES);
        taskHeader.setLastUpdated(new Date());
        taskHeader.setLastUpdatedBy(QuantityConstant.PLATFORM_MES);
        taskHeader.setTaskNo(taskNo);
        taskHeader.setIsBack(QuantityConstant.MES_TASK_NOT_BACK);
        boolean success2 = taskHeaderService.save(taskHeader);
        if (!success2) {
            throw new ServiceException("抽样检验出库失败,保存任务头表失败");
        }
        List<TaskDetail> taskDetailList = new LinkedList<>();
        LambdaQueryWrapper<ShipmentContainerDetail> shipmentContainerQueryWrapper = Wrappers.lambdaQuery();
        shipmentContainerQueryWrapper.eq(ShipmentContainerDetail::getShippingContainerId, shipmentContainerHeader.getId());
        List<ShipmentContainerDetail> shipmentContainerDetailList = shipmentContainerDetailService.list(shipmentContainerQueryWrapper);
        shipmentContainerDetailList.forEach(shipmentContainerDetail -> {
            // 任务明细
            TaskDetail taskDetail = new TaskDetail();
            BeanUtils.copyBeanProp(taskDetail, shipmentContainerDetail);
            taskDetail.setTaskId(taskHeader.getId());
            taskDetail.setTaskType(taskHeader.getTaskType());
            taskDetail.setInternalTaskType(taskHeader.getInternalTaskType());
            taskDetail.setAllocationId(finalShipmentContainerHeader.getId());
            taskDetail.setBillCode(shipmentHeader.getCode());
            taskDetail.setBillDetailId(shipmentContainerDetail.getShipmentDetailId());
            taskDetail.setFromInventoryId(shipmentContainerDetail.getInventoryId());
            taskDetail.setToInventoryId(shipmentContainerDetail.getInventoryId());
            taskDetail.setAllocationId(shipmentContainerDetail.getId());
            taskDetail.setFromLocation(taskHeader.getFromLocation());
            taskDetail.setStatus(QuantityConstant.TASK_STATUS_BUILD);
            taskDetail.setCreated(new Date());
            taskDetail.setCreatedBy(QuantityConstant.PLATFORM_MES);
            taskDetail.setLastUpdated(new Date());
            taskDetail.setLastUpdatedBy(QuantityConstant.PLATFORM_MES);
            taskDetailList.add(taskDetail);
        });
        success2 = taskDetailService.saveBatch(taskDetailList);
        if (!success2) {
            throw new ServiceException("抽样检验出库失败,保存任务明细表失败");
        }
        // 锁定库位和载具
        success2 = containerService.updateStatus(shipmentContainerHeader.getContainerCode(), QuantityConstant.STATUS_CONTAINER_LOCK);
        if (!success2) {
            throw new ServiceException("抽样检验出库失败,锁定载具失败");
        }
        success2 = locationService.updateStatus(shipmentContainerHeader.getLocationCode(), QuantityConstant.STATUS_LOCATION_LOCK);
        if (!success2) {
            throw new ServiceException("抽样检验出库失败,锁定原库位失败");
        }
        // 更新库存任务数
        List<InventoryDetail> updateInventoryDetailList = new LinkedList<>();
        inventoryDetailList.forEach(inventoryDetail -> {
            InventoryDetail updateInventoryDetail = new InventoryDetail();
            updateInventoryDetail.setId(inventoryDetail.getId());
            updateInventoryDetail.setTaskQty(inventoryDetail.getQty());
            updateInventoryDetailList.add(updateInventoryDetail);
        });
        success = inventoryDetailService.updateBatchById(updateInventoryDetailList);
        if (!success) {
            throw new ServiceException("抽样检验出库失败,更新库存任务数量失败");
        }
        return AjaxResult.success("抽样检验出库成功");
    }

    @Override
    public AjaxResult receiptByQC(MesReceiptByQCDto mesReceiptByQCDto) {
        // 参数校验
        String taskNo = mesReceiptByQCDto.getTaskNo();
        Integer materialInspectStatus = mesReceiptByQCDto.getMaterialInspectStatus();
        String fromPort = mesReceiptByQCDto.getFromPort();
        String orderCode = mesReceiptByQCDto.getOrderCode();
        String containerCode = mesReceiptByQCDto.getContainerCode();
        String vehicleCode = mesReceiptByQCDto.getVehicleCode();
        List<ReceiptMaterialData> materialDataList = mesReceiptByQCDto.getMaterialDataList();
        Integer changeRange = mesReceiptByQCDto.getChangeRange();
        if (StringUtils.isEmpty(taskNo)) {
            return AjaxResult.error("抽样质检余料退回失败,任务号为空");
        }
        if (StringUtils.isNull(materialInspectStatus)) {
            return AjaxResult.error("抽样质检余料退回失败,物料状态为空");
        }
        if (StringUtils.isEmpty(fromPort)) {
            return AjaxResult.error("抽样质检余料退回失败,起始料点为空");
        }
        if (StringUtils.isEmpty(orderCode)) {
            return AjaxResult.error("抽样质检余料退回失败,工单号为空");
        }
        if (StringUtils.isEmpty(containerCode)) {
            return AjaxResult.error("抽样质检余料退回失败,载具编码为空");
        }
        if (StringUtils.isEmpty(vehicleCode)) {
            return AjaxResult.error("抽样质检余料退回失败,盛具编码为空");
        }
        if (StringUtils.isEmpty(materialDataList)) {
            return AjaxResult.error("抽样质检余料退回失败,物料信息为空");
        }
        if (Objects.isNull(changeRange)) {
            return AjaxResult.error("抽样质检余料退回失败,抽检范围为空");
        }
        // 校验载具
        Container container = containerService.getByCode(containerCode);
        if (StringUtils.isNull(container)) {
            throw new ServiceException(StringUtils.format("入库失败,载具编码:{} 不存在", containerCode));
        }
        if (StringUtils.isNotEmpty(container.getVehicleCode()) &&
                !container.getVehicleCode().equals(vehicleCode)) {
            throw new ServiceException(StringUtils.format("入库失败,载具:{} 已和盛具:{} 绑定",
                    container.getCode(), container.getVehicleCode()));
        }
        Location location = locationService.getLocationByContainerCode(containerCode);
        if (location != null) {
            throw new ServiceException(StringUtils.format("入库失败,载具:{} 已在库位:{} 上", containerCode, location.getCode()));
        }
        InventoryHeader inventoryHeader = inventoryHeaderService.getByContainerCode(containerCode);
        if (inventoryHeader != null) {
            throw new ServiceException(StringUtils.format("入库失败,载具:{} 已经存在库存", containerCode));
        }
        // 校验盛具
        Vehicle vehicle = vehicleService.getVehicleByCode(vehicleCode);
        if (StringUtils.isNull(vehicle)) {
            throw new ServiceException(StringUtils.format("入库失败,不存在盛具编码 {}", vehicleCode));
        }
        Container containerByVehicleCode = containerService.getByVehicleCode(vehicleCode);
        if (containerByVehicleCode != null) {
            throw new ServiceException(StringUtils.format("入库失败,盛具:{} 已和载具:{} 绑定",
                    vehicleCode, containerByVehicleCode.getCode()));
        }
        for (ReceiptMaterialData data : mesReceiptByQCDto.getMaterialDataList()) {
            if (Boolean.FALSE.equals(data.getIsMaterial())) {
                continue;
            }
            Material material = materialService.getByCode(data.getMaterialCode());
            if (material == null) {
                throw new ServiceException(StringUtils.format("入库失败,根据物料编码:{} 没有找到对应物料信息", data.getMaterialCode()));
            }
            if (StringUtils.isNull(data.getLocationNoX())) {
                throw new ServiceException("入库失败,物料有 x 坐标为空的,请检查");
            }
            if (StringUtils.isNull(data.getLocationNoY())) {
                throw new ServiceException("入库失败,物料有 y 坐标为空的,请检查");
            }
            if (StringUtils.isNull(data.getQty()) || data.getQty().equals(BigDecimal.ZERO)) {
                throw new ServiceException(StringUtils.format("入库失败,坐标 {},{} 的物料数量为空或为0",
                        data.getLocationNoX().toString(), data.getLocationNoY().toString()));
            }
            if (StringUtils.isEmpty(data.getTracingNo())) {
                throw new ServiceException(StringUtils.format("入库失败,坐标:{},{} 的物料追溯码为空",
                        data.getLocationNoX().toString(), data.getLocationNoY().toString()));
            }
            if (StringUtils.isNull(data.getQualityStatus())) {
                throw new ServiceException(StringUtils.format("入库失败,坐标:{},{} 的物料质量状态为空",
                        data.getLocationNoX().toString(), data.getLocationNoY().toString()));
            }
            // 校验物料绑定的盛具类型
            if (!vehicle.getVehicleType().equals(material.getVehicleTypeCode())) {
                throw new ServiceException(StringUtils.format("入库失败,入库盛具类型 {} 与物料 {} 绑定的盛具类型 {} 不一致",
                        vehicle.getVehicleType(), material.getName(), material.getVehicleTypeCode()));
            }
            // 校验物料是否入立库(根据是否物料分区判断)
            if (StringUtils.isEmpty(material.getType())) {
                throw new ServiceException(StringUtils.format("入库失败,物料:{} 未设置物料类别", material.getName()));
            }
            MaterialType materialType = materialTypeService.getByCode(material.getType());
            if (materialType == null) {
                throw new ServiceException(StringUtils.format("入库失败,物料类别编码 {} 不存在", material.getType()));
            }
            String materialAreaCode = materialType.getMaterialAreaCode();
            if (StringUtils.isEmpty(materialAreaCode)) {
                throw new ServiceException(StringUtils.format("入库失败,物料:{} 所属类别:{} 未配置物料分区", material.getName(), materialType.getName()));
            }
        }
        // 校验同一个xy坐标的物料 数量只能为1
        if (!QuantityConstant.VEHICLE_TYPE_COMMON.equals(vehicleCode)) {
            ReceiptMaterialData qtyNotZeroMaterial = materialDataList.stream()
                    .filter(x -> x.getQty().compareTo(BigDecimal.ONE) != 0)
                    .findAny()
                    .orElse(null);
            if (StringUtils.isNotNull(qtyNotZeroMaterial)) {
                throw new ServiceException(StringUtils.format("入库失败,物料坐标 {},{} 的数量异常,请检查数量",
                        qtyNotZeroMaterial.getLocationNoX(),
                        qtyNotZeroMaterial.getLocationNoY()));
            }
            Map<String, List<ReceiptMaterialData>> materialGroupByXyMap = materialDataList.stream()
                    .collect(Collectors.groupingBy(material -> material.getLocationNoX().toString().concat(",").concat(material.getLocationNoY().toString())));
            for (Map.Entry<String, List<ReceiptMaterialData>> entry : materialGroupByXyMap.entrySet()) {
                if (entry.getValue().size() > 1) {
                    throw new ServiceException(StringUtils.format("入库失败,物料坐标 {} 重复,请检查坐标", entry.getKey()));
                }
            }
        }
        String receiptType;
        switch (materialInspectStatus) {
            case 1:
                receiptType = QuantityConstant.RECEIPT_TYPE_QC_SELF;
                break;
            case 3:
                receiptType = QuantityConstant.RECEIPT_TYPE_QC_OFFICE;
                break;
            case 4:
                receiptType = QuantityConstant.RECEIPT_TYPE_QC_ARMY;
                break;
            default:
                throw new ServiceException(StringUtils.format("检验状态:{} 不存在", materialInspectStatus));
        }
        MesOrder mesOrder = new MesOrder();
        mesOrder.setTaskNo(taskNo);
        mesOrder.setOrderCode(orderCode);
        mesOrder.setMaterialDataList(materialDataList);
        mesOrder.setReceiptType(receiptType);
        AjaxResult ajaxResult = mesService.receiptOrder(mesOrder);
        if (Boolean.TRUE.equals(ajaxResult.hasErr())) {
            throw new ServiceException(ajaxResult.getMsg());
        }
        // 新建保存组盘头表记录
        LambdaQueryWrapper<ReceiptContainerHeader> lambda = Wrappers.lambdaQuery();
        lambda.eq(ReceiptContainerHeader::getContainerCode, containerCode)
                .eq(ReceiptContainerHeader::getStatus, QuantityConstant.RECEIPT_CONTAINER_BUILD);
        ReceiptContainerHeader receiptContainerHeader = receiptContainerHeaderService.getOne(lambda);

        if (StringUtils.isNull(receiptContainerHeader)) {
            receiptContainerHeader = new ReceiptContainerHeader();
            receiptContainerHeader.setReceiptCode(ajaxResult.getData().toString());
            receiptContainerHeader.setWarehouseCode(QuantityConstant.DEFAULT_WAREHOUSE);
            receiptContainerHeader.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
            receiptContainerHeader.setContainerCode(containerCode);
            receiptContainerHeader.setTaskType(QuantityConstant.TASK_TYPE_WHOLERECEIPT);
            receiptContainerHeader.setCreatedBy(QuantityConstant.PLATFORM_MES);
            receiptContainerHeader.setLastUpdatedBy(QuantityConstant.PLATFORM_MES);
            receiptContainerHeader.setTaskNo(taskNo);
            receiptContainerHeader.setOrderCode(orderCode);
            receiptContainerHeader.setVehicleCode(vehicleCode);
            receiptContainerHeader.setPort(fromPort);
            receiptContainerHeader.setChangeRange(changeRange);

            WorkOrderHeader workOrderHeader = workOrderHeaderService.getOrderByCode(orderCode);
            if (workOrderHeader != null) {
                receiptContainerHeader.setBatchNumber(workOrderHeader.getBatchNumber());
                receiptContainerHeader.setOrderNumber(workOrderHeader.getOrderNumber());
            }
            if (!receiptContainerHeaderService.save(receiptContainerHeader)) {
                throw new ServiceException("入库失败,入库组盘头表保存失败");
            }
            // 组盘的时候,将载具和盛具绑定
            if (!containerService.updateVehicle(container.getCode(), vehicle.getCode(), vehicle.getVehicleType())) {
                throw new ServiceException("入库失败,载具与盛具绑定失败");
            }
        } else {
            if (receiptContainerHeader.getStatus() >= QuantityConstant.RECEIPT_CONTAINER_TASK && receiptContainerHeader.getStatus() < QuantityConstant.RECEIPT_CONTAINER_FINISHED) {
                throw new ServiceException("入库失败,载具已经生成任务,不能放物料了!");
            }
        }
        LambdaQueryWrapper<ReceiptHeader> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ReceiptHeader::getCode, ajaxResult.getData());
        ReceiptHeader receiptHeader = receiptHeaderService.getOne(wrapper);
        if (receiptHeader == null) {
            throw new ServiceException(StringUtils.format("入库失败,未找到上游单号为 {} 的入库单", taskNo));
        }
        //入库单id查询入库单详情
        LambdaQueryWrapper<ReceiptDetail> receiptDetailQueryWrapper = Wrappers.lambdaQuery();
        receiptDetailQueryWrapper.eq(ReceiptDetail::getReceiptId, receiptHeader.getId());
        List<ReceiptDetail> receiptDetailList = receiptDetailService.list(receiptDetailQueryWrapper);
        if (StringUtils.isEmpty(receiptDetailList)) {
            throw new ServiceException(StringUtils.format("入库失败,未找到 {} 入库单的详情", receiptHeader.getId()));
        }
        //入库单详情的物料条码比较物料入库的物料条码
        for (ReceiptDetail receiptDetail : receiptDetailList) {
            //更新入库单详情的收货数量
            if (!receiptDetailService.updateById(receiptDetail)) {
                throw new ServiceException("入库失败,更新入库单详情失败");
            }
            //如果单据数量等于已收数量,更新入库详情状态和入库单状态
            if (receiptDetail.getQty().compareTo(receiptDetail.getTaskQty()) == 0) {
                receiptDetail.setProcessStamp(QuantityConstant.RECEIPT_HEADER_RECEIVING.toString());
                if (!receiptDetailService.updateById(receiptDetail)) {
                    throw new ServiceException("入库失败,更新入库详情处理标记失败");
                }
            }
            receiptDetailService.updateReceiptHeaderLastStatus(receiptDetail.getReceiptId());
        }
        List<ReceiptContainerDetail> detailList = new ArrayList<>();
        //入库组盘详情
        for (ReceiptMaterialData data : materialDataList) {
            Material material = materialService.getByCode(data.getMaterialCode());
            ReceiptDetail receiptDetail = receiptDetailList.stream()
                    .filter(x -> x.getMaterialCode().equals(data.getMaterialCode()))
                    .findFirst().orElseThrow(() -> new ServiceException(StringUtils.format("入库失败,入库单:{} 明细中不存在物料:{}", receiptHeader.getCode(), data.getMaterialCode())));
            ReceiptContainerDetail receiptContainerDetail = new ReceiptContainerDetail();
            BeanUtils.copyBeanProp(receiptContainerDetail, data);
            receiptContainerDetail.setReceiptContainerId(receiptContainerHeader.getId());
            receiptContainerDetail.setWarehouseCode(receiptContainerHeader.getWarehouseCode());
            receiptContainerDetail.setId(null);
            receiptContainerDetail.setReceiptId(receiptDetail.getReceiptId());
            receiptContainerDetail.setReceiptDetailId(receiptDetail.getId());
            receiptContainerDetail.setReceiptCode(receiptDetail.getReceiptCode());
            receiptContainerDetail.setBatch(receiptDetail.getBatch());
            receiptContainerDetail.setTaskType(receiptContainerHeader.getTaskType());
            receiptContainerDetail.setContainerCode(containerCode);
            receiptContainerDetail.setContainerType(container.getContainerType());
            receiptContainerDetail.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
            receiptContainerDetail.setMaterialCode(material.getCode());
            receiptContainerDetail.setMaterialName(material.getName());
            receiptContainerDetail.setMaterialSpec(material.getSpec());
            receiptContainerDetail.setMaterialUnit(material.getUnit());
            receiptContainerDetail.setWeight(material.getWeight());
            receiptContainerDetail.setHigh(material.getHigh());
            receiptContainerDetail.setDescription(material.getDescription());
            receiptContainerDetail.setInventorySts(Boolean.TRUE.equals(data.getQualityStatus()) ?
                    QuantityConstant.INVENTORY_STATUS_OK : QuantityConstant.INVENTORY_STATUS_NG);
            receiptContainerDetail.setMaterialInspectStatus(materialInspectStatus);
            receiptContainerDetail.setCreatedBy(QuantityConstant.PLATFORM_MES);
            receiptContainerDetail.setLastUpdatedBy(QuantityConstant.PLATFORM_MES);
            detailList.add(receiptContainerDetail);
        }
        if (!receiptContainerDetailService.saveBatch(detailList)) {
            throw new ServiceException("入库失败,保存入库组盘详情失败");
        }
        //生成入库任务
        List<Integer> idList = Arrays.asList(Convert.toIntArray(String.valueOf(receiptContainerHeader.getId())));
        ajaxResult = receiptTaskService.createReceiptTask(idList);
        if (Boolean.TRUE.equals(ajaxResult.hasErr())) {
            throw new ServiceException(ajaxResult.getMsg());
        }
        return AjaxResult.success("抽样质检余料退回成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult movingMaterial(MesMovingMaterialDto mesMovingMaterialDto) {
        String materialCode = mesMovingMaterialDto.getMaterialCode();
        String sourceOrderCode = mesMovingMaterialDto.getSourceOrderCode();
        String targetOrderCode = mesMovingMaterialDto.getTargetOrderCode();
        BigDecimal qty = mesMovingMaterialDto.getQty();
        if (StringUtils.isEmpty(materialCode)) {
            return AjaxResult.error("挪料失败,物料编码为空");
        }
        if (StringUtils.isEmpty(sourceOrderCode)) {
            return AjaxResult.error("挪料失败,源工单号为空");
        }
        if (StringUtils.isEmpty(targetOrderCode)) {
            return AjaxResult.error("挪料失败,目标工单号为空");
        }
        if (Objects.isNull(qty)) {
            return AjaxResult.error("挪料失败,挪动数量为空");
        }

        LambdaQueryWrapper<InventoryDetail> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(InventoryDetail::getOrderCode, sourceOrderCode);
        List<InventoryDetail> inventoryDetailList = inventoryDetailService.list(queryWrapper);
        if (StringUtils.isEmpty(inventoryDetailList)) {
            return AjaxResult.error(StringUtils.format("挪料失败,工单号:{} 下没有对应库存", sourceOrderCode));
        }
        List<String> contaienrCodeList = inventoryDetailList.stream()
                .map(InventoryDetail::getContainerCode)
                .distinct()
                .collect(Collectors.toList());
        List<String> unAvailableContainerCodeList = new ArrayList<>();
        for (String containerCode : contaienrCodeList) {
            Container container = containerService.getByCode(containerCode);
            if (!Objects.isNull(container)) {
                if (QuantityConstant.STATUS_CONTAINER_LOCK.equals(container.getStatus())) {
                    unAvailableContainerCodeList.add(containerCode);
                }
            } else {
                return AjaxResult.error(StringUtils.format("挪料失败,载具:{} 不存在", containerCode));
            }
        }
        List<InventoryDetail> availableInventoryDetailList = inventoryDetailList.stream()
                .filter(x -> !unAvailableContainerCodeList.contains(x.getContainerCode()))
                .collect(Collectors.toList());
        BigDecimal availableQty = availableInventoryDetailList.stream().map(InventoryDetail::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (availableQty.compareTo(qty) < 0) {
            return AjaxResult.error(StringUtils.format("挪料失败,库存可用数量:{} 小于挪用数量:{}", availableQty, qty));
        }
        availableInventoryDetailList.sort(Comparator.comparing(InventoryDetail::getQty));
        Map<String, List<InventoryDetail>> map = availableInventoryDetailList.stream()
                .collect(Collectors.groupingBy(InventoryDetail::getContainerCode));
        List<InventoryDetail> updateInventoryDetailList = new ArrayList<>();
        for (Map.Entry<String, List<InventoryDetail>> entry : map.entrySet()) {
            List<InventoryDetail> value = entry.getValue();
            BigDecimal qtySum = value.stream().map(InventoryDetail::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);
            if (qtySum.compareTo(qty) <= 0) {
                updateInventoryDetailList.addAll(value);
            } else {
                qty = qty.subtract(qtySum);
            }
        }
        LambdaUpdateWrapper<InventoryDetail> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.in(InventoryDetail::getId, updateInventoryDetailList.stream().map(InventoryDetail::getId).collect(Collectors.toList()))
                .set(InventoryDetail::getOrderCode, targetOrderCode);
        boolean success = inventoryDetailService.update(updateWrapper);
        if (!success) {
            throw new ServiceException("挪料失败,更新库存失败");
        }
        return AjaxResult.success("挪料成功");
    }

    @Override
    public AjaxResult syncWorkOrder(MesWorkOrder workOrder) {
        String taskNo = workOrder.getTaskNo();
        if (StringUtils.isEmpty(taskNo)) {
            return AjaxResult.error("同步工单状态失败,任务号为空");
        }
        String orderCode = workOrder.getOrderCode();
        if (StringUtils.isEmpty(orderCode)) {
            return AjaxResult.error("同步工单状态失败,工单号为空");
        }
        Integer orderStatus = workOrder.getOrderStatus();
        if (Objects.isNull(orderStatus)) {
            return AjaxResult.error("同步工单状态失败,工单状态为空");
        }
        WorkOrderHeader workOrderHeader = workOrderHeaderService.getOrderByCode(orderCode);
        if (workOrderHeader == null) {
            return AjaxResult.error(StringUtils.format("同步工单状态失败,工单:{} 不存在", orderCode));
        }
        WorkOrderHeader updateWorkOrderHeader = new WorkOrderHeader();
        updateWorkOrderHeader.setId(workOrderHeader.getId());
        updateWorkOrderHeader.setOrderStatus(orderStatus);
        boolean success = workOrderHeaderService.updateById(updateWorkOrderHeader);
        if (success) {
            return AjaxResult.success("同步工单状态成功");
        } else {
            return AjaxResult.error(StringUtils.format("同步工单状态失败,工单号:{}", orderCode));
        }
    }

    @Override
    public AjaxResult getMesTracingCode(String zs) {
        String url = addressService.selectAddress(QuantityConstant.MES_GET_TRACINGNO, QuantityConstant.DEFAULT_WAREHOUSE, QuantityConstant.DEFAULT_AREA);
        /*Map<String, Object> map = new HashMap<>();
        map.put("tracingCode", zs);
        String jsonParam = JSON.toJSONString(map);*/
        String jsonParam = '"' + zs + '"';
        ResponseEntity<JSONObject> result = RestUtil.request_post(url, QuantityConstant.DEFAULT_WAREHOUSE, jsonParam);
        if (result != null && result.getBody() != null) {
            boolean status = result.getBody().getBoolean("status");
            String msg = result.getBody().getString("message");
            if (!status) {
                return AjaxResult.error(msg);
            }
            String data = result.getBody().getString("data");
            return AjaxResult.success(msg).setData(data);
        } else {
            throw new ServiceException("获取MES追溯码失败,接口地址错误或返回为空");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult clearStock(ClearStockDto clearStockDto) {
        String orderCode = clearStockDto.getOrderCode();
        String materialCode = clearStockDto.getMaterialCode();
        String port = clearStockDto.getPort();
        if (StringUtils.isEmpty(orderCode)) {
            return AjaxResult.error("清库失败,工单号为空");
        }
        if (StringUtils.isEmpty(materialCode)) {
            return AjaxResult.error("清库失败,物料编码为空");
        }
        if (StringUtils.isEmpty(port)) {
            return AjaxResult.error("清库失败,出库口为空");
        }
        Material material = materialService.getByCode(materialCode);
        if (material == null) {
            return AjaxResult.error(StringUtils.format("清库失败,根据物料编码:{} 未找到物料", materialCode));
        }
        LambdaQueryWrapper<InventoryHeader> inventoryHeaderQueryWrapper = Wrappers.lambdaQuery();
        inventoryHeaderQueryWrapper.eq(InventoryHeader::getOrderCode, orderCode)
                .ne(InventoryHeader::getZoneCode, QuantityConstant.ZONE_TYPE_CBG)
                .eq(InventoryHeader::getContainerStatus, QuantityConstant.STATUS_CONTAINER_SOME);
        List<InventoryHeader> inventoryHeaderList = inventoryHeaderService.list(inventoryHeaderQueryWrapper);
        inventoryHeaderList = inventoryHeaderList.stream().filter(x -> x.getContainerStatus().equals(QuantityConstant.STATUS_CONTAINER_SOME)).collect(Collectors.toList());
        if (inventoryHeaderList.isEmpty()) {
            return AjaxResult.error(StringUtils.format("清库失败,库内暂无工单号为:{} 的可用库存", orderCode));
        }
        LambdaQueryWrapper<InventoryDetail> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.in(InventoryDetail::getInventoryHeaderId, inventoryHeaderList.stream().map(InventoryHeader::getId).collect(Collectors.toList()))
                .eq(InventoryDetail::getMaterialCode, materialCode);
        List<InventoryDetail> inventoryDetailList = inventoryDetailService.list(queryWrapper);
        if (inventoryDetailList.isEmpty()) {
            return AjaxResult.error(StringUtils.format("清库失败,库内暂无工单号为:{},物料编码为:{} 的可用库存", orderCode, materialCode));
        }
        BigDecimal clearSumQty = inventoryDetailList.stream().map(InventoryDetail::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);

        ShipmentHeader shipmentHeader = new ShipmentHeader();
        shipmentHeader.setShipmentType(QuantityConstant.SHIPMENT_BILL_TYPE_SCRAP);
        shipmentHeader.setTotalQty(clearSumQty);
        shipmentHeader.setTotalLines(1);
        // 生成单据头
        String code = shipmentHeaderService.createCode(shipmentHeader.getShipmentType());
        shipmentHeader.setCode(code);
        shipmentHeader.setFirstStatus(QuantityConstant.SHIPMENT_HEADER_GROUPDISK);
        shipmentHeader.setLastStatus(QuantityConstant.SHIPMENT_HEADER_GROUPDISK);
        shipmentHeader.setLastUpdatedBy(StringUtils.isNotEmpty(ShiroUtils.getLoginName()) ? ShiroUtils.getLoginName() : QuantityConstant.PLATFORM_MES);
        shipmentHeader.setCreatedBy(StringUtils.isNotEmpty(ShiroUtils.getLoginName()) ? ShiroUtils.getLoginName() : QuantityConstant.PLATFORM_MES);
        shipmentHeader.setWarehouseCode(QuantityConstant.DEFAULT_WAREHOUSE);
        shipmentHeader.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
        boolean success = shipmentHeaderService.save(shipmentHeader);
        ShipmentDetail shipmentDetail = this.createShipmentDetail(materialCode, clearSumQty, shipmentHeader);
        // 生成单据明细
        success = shipmentDetailService.save(shipmentDetail);
        if (!success) {
            throw new ServiceException("清库失败,保存出库单明细失败");
        }
        List<InventoryHeader> shipInventoryHeader = inventoryHeaderList.stream().filter(inventoryHeader -> inventoryDetailList.stream()
                        .map(InventoryDetail::getInventoryHeaderId)
                        .collect(Collectors.toList())
                        .contains(inventoryHeader.getId()))
                .collect(Collectors.toList());
        for (InventoryHeader inventoryHeader : shipInventoryHeader) {
            ShipmentContainerHeader shipmentContainerHeader = this.createShipmentContainerHeader(inventoryHeader, port, shipmentHeader);
            // 生成配盘头
            success = shipmentContainerHeaderService.save(shipmentContainerHeader);
            if (!success) {
                throw new ServiceException("清库失败,保存出库配盘头失败");
            }
            List<InventoryDetail> inventoryDetails = inventoryDetailList.stream()
                    .filter(inventoryDetail -> inventoryDetail.getInventoryHeaderId().equals(inventoryHeader.getId()))
                    .collect(Collectors.toList());
            List<ShipmentContainerDetail> shipmentContainerDetailList = this.createShipmentContainerDetail(shipmentContainerHeader, shipmentDetail, inventoryDetails);
            // 生成配盘明细
            success = shipmentContainerDetailService.saveBatch(shipmentContainerDetailList);
            if (!success) {
                throw new ServiceException("清库失败,保存出库配盘明细败");
            }
            TaskHeader taskHeader = this.createTaskHeader(shipmentContainerHeader, inventoryHeader);
            success = taskHeaderService.save(taskHeader);
            if (!success) {
                throw new ServiceException("清库失败,保存任务头表失败");
            }
            List<TaskDetail> taskDetailList = this.createTaskDetail(taskHeader, shipmentDetail, shipmentContainerDetailList);
            success = taskDetailService.saveBatch(taskDetailList);
            if (!success) {
                throw new ServiceException("清库失败,保存任务明细表失败");
            }
            // 锁定载具和库位
            success = containerService.updateStatus(inventoryHeader.getContainerCode(), QuantityConstant.STATUS_CONTAINER_LOCK);
            if (!success) {
                throw new ServiceException("清库失败,锁定载具失败");
            }
            success = locationService.updateStatus(inventoryHeader.getLocationCode(), QuantityConstant.STATUS_LOCATION_LOCK);
            if (!success) {
                throw new ServiceException("清库失败,锁定库位失败");
            }
            InventoryHeader updateInventoryHeader = new InventoryHeader();
            updateInventoryHeader.setId(inventoryHeader.getId());
            updateInventoryHeader.setContainerStatus(QuantityConstant.STATUS_CONTAINER_LOCK);
            updateInventoryHeader.setLastUpdatedBy(StringUtils.isNotEmpty(ShiroUtils.getLoginName()) ? ShiroUtils.getLoginName() : QuantityConstant.PLATFORM_MES);
            success = inventoryHeaderService.updateById(updateInventoryHeader);
            if (!success) {
                throw new ServiceException("清库失败,锁定库存容器失败");
            }
            // 更新库存任务数量
            List<InventoryDetail> updateInventoryDetailList = new ArrayList<>();
            inventoryDetails.forEach(inventoryDetail -> {
                InventoryDetail updateInventoryDetail = new InventoryDetail();
                updateInventoryDetail.setId(inventoryDetail.getId());
                updateInventoryDetail.setTaskQty(inventoryDetail.getQty());
                updateInventoryDetailList.add(updateInventoryDetail);
            });
            success = inventoryDetailService.updateBatchById(updateInventoryDetailList);
            if (!success) {
                throw new ServiceException("清库失败,更新库存占用数量失败");
            }
        }
        return AjaxResult.success("清库成功");
    }

    private ShipmentDetail createShipmentDetail(String materialCode, BigDecimal qty, ShipmentHeader shipmentHeader) {
        Material material = materialService.getByCode(materialCode);
        if (material == null) {
            throw new ServiceException(StringUtils.format("根据物料编码:{} 未找到物料", materialCode));
        }
        ShipmentDetail shipmentDetail = new ShipmentDetail();
        shipmentDetail.setShipmentId(shipmentHeader.getId());
        shipmentDetail.setWarehouseCode(shipmentHeader.getWarehouseCode());
        shipmentDetail.setCompanyCode(shipmentHeader.getCompanyCode());
        shipmentDetail.setShipmentCode(shipmentHeader.getCode());
        shipmentDetail.setMaterialCode(material.getCode());
        shipmentDetail.setMaterialName(material.getName());
        shipmentDetail.setMaterialSpec(material.getSpec());
        shipmentDetail.setMaterialUnit(material.getUnit());
        shipmentDetail.setDescription(material.getDescription());
        shipmentDetail.setWeight(material.getWeight());
        shipmentDetail.setQty(qty);
        shipmentDetail.setTaskQty(qty);
        shipmentDetail.setInventorySts(QuantityConstant.INVENTORY_STATUS_OK);
        shipmentDetail.setStatus(shipmentHeader.getLastStatus());
        shipmentDetail.setCreated(new Date());
        shipmentDetail.setCreatedBy(shipmentHeader.getCreatedBy());
        shipmentDetail.setLastUpdated(new Date());
        shipmentDetail.setLastUpdatedBy(shipmentHeader.getLastUpdatedBy());
        return shipmentDetail;
    }

    private ShipmentContainerHeader createShipmentContainerHeader(InventoryHeader inventoryHeader, String port, ShipmentHeader shipmentHeader) {
        ShipmentContainerHeader shipmentContainerHeader = new ShipmentContainerHeader();
        BeanUtils.copyBeanProp(shipmentContainerHeader, inventoryHeader);
        shipmentContainerHeader.setTaskType(QuantityConstant.TASK_TYPE_WHOLESHIPMENT);
        shipmentContainerHeader.setStatus(QuantityConstant.SHIPMENT_CONTAINER_TASK);
        shipmentContainerHeader.setPort(port);
        shipmentContainerHeader.setCreated(new Date());
        shipmentContainerHeader.setCreatedBy(shipmentHeader.getCreatedBy());
        shipmentContainerHeader.setLastUpdated(new Date());
        shipmentContainerHeader.setLastUpdatedBy(shipmentHeader.getLastUpdatedBy());
        shipmentContainerHeader.setShipmentCode(shipmentHeader.getCode());
        return shipmentContainerHeader;
    }

    private List<ShipmentContainerDetail> createShipmentContainerDetail(ShipmentContainerHeader shipmentContainerHeader,
                                                                        ShipmentDetail shipmentDetail, List<InventoryDetail> inventoryDetailList) {
        List<ShipmentContainerDetail> shipmentContainerDetailList = new ArrayList<>();
        inventoryDetailList.forEach(inventoryDetail -> {
            ShipmentContainerDetail shipmentContainerDetail = new ShipmentContainerDetail();
            BeanUtils.copyBeanProp(shipmentContainerDetail, inventoryDetail);
            shipmentContainerDetail.setShippingContainerId(shipmentContainerHeader.getId());
            shipmentContainerDetail.setInventoryId(inventoryDetail.getId());
            shipmentContainerDetail.setShipmentCode(shipmentContainerHeader.getShipmentCode());
            shipmentContainerDetail.setShipmentId(shipmentDetail.getShipmentId());
            shipmentContainerDetail.setShipmentDetailId(shipmentDetail.getId());
            shipmentContainerDetail.setStatus(shipmentContainerHeader.getStatus());
            shipmentContainerDetail.setCreated(new Date());
            shipmentContainerDetail.setCreatedBy(shipmentContainerHeader.getCreatedBy());
            shipmentContainerDetail.setLastUpdated(new Date());
            shipmentContainerDetail.setLastUpdatedBy(shipmentContainerHeader.getLastUpdatedBy());
            shipmentContainerDetailList.add(shipmentContainerDetail);
        });
        return shipmentContainerDetailList;
    }

    private TaskHeader createTaskHeader(ShipmentContainerHeader shipmentContainerHeader, InventoryHeader inventoryHeader) {
        TaskHeader taskHeader = new TaskHeader();
        BeanUtils.copyBeanProp(taskHeader, shipmentContainerHeader);
        taskHeader.setZoneCode(inventoryHeader.getZoneCode());
        taskHeader.setAllocationHeadId(shipmentContainerHeader.getId());
        taskHeader.setInternalTaskType(QuantityConstant.TASK_INTENERTYPE_SHIPMENT);
        taskHeader.setFromLocation(shipmentContainerHeader.getLocationCode());
        taskHeader.setStatus(QuantityConstant.TASK_STATUS_BUILD);
        taskHeader.setPort(shipmentContainerHeader.getPort());
        taskHeader.setCreated(new Date());
        taskHeader.setLastUpdated(new Date());
        return taskHeader;
    }

    private List<TaskDetail> createTaskDetail(TaskHeader taskHeader, ShipmentDetail shipmentDetail, List<ShipmentContainerDetail> shipmentContainerDetailList) {
        List<TaskDetail> taskDetailList = new ArrayList<>();
        shipmentContainerDetailList.forEach(shipmentContainerDetail -> {
            TaskDetail taskDetail = new TaskDetail();
            BeanUtils.copyBeanProp(taskDetail, shipmentContainerDetail);
            taskDetail.setTaskId(taskHeader.getId());
            taskDetail.setTaskType(taskHeader.getTaskType());
            taskDetail.setInternalTaskType(taskHeader.getInternalTaskType());
            taskDetail.setAllocationId(shipmentContainerDetail.getId());
            taskDetail.setBillDetailId(shipmentDetail.getId());
            taskDetail.setBillCode(shipmentDetail.getShipmentCode());
            taskDetail.setToInventoryId(shipmentContainerDetail.getInventoryId());
            taskDetail.setStatus(taskHeader.getStatus());
            taskDetail.setCreated(new Date());
            taskDetail.setLastUpdated(new Date());
            taskDetailList.add(taskDetail);
        });
        return taskDetailList;
    }

    @Override
    public AjaxResult syncMaterial(List<SyncMaterialDto> syncMaterialDtoList) {
        int materialCodeQty = 0, materialNameQty = 0, vehicleTypeQty = 0, materialTypeQty = 0, enableQty = 0, saveQty = 0, updateQty = 0;
        // 查询该物料是否已存在:存在则更新,不存在则新增
        for (SyncMaterialDto syncMaterialDto : syncMaterialDtoList) {
            // 数据校验
            String materialCode = syncMaterialDto.getMaterialCode();
            String materialName = syncMaterialDto.getMaterialName();
            //String materialSpec = syncMaterialDto.getMaterialSpec();
            //String materialDescription = syncMaterialDto.getDescription();
            String vehicleType = syncMaterialDto.getVehicleType();
            String materialType = syncMaterialDto.getMaterialType();
            //Integer supplyType = syncMaterialDto.getSupplyType();
            Boolean enable = syncMaterialDto.getEnable();
            if (StringUtils.isEmpty(materialCode)) {
                materialCodeQty++;
                continue;
            }
            if (StringUtils.isEmpty(materialName)) {
                materialNameQty++;
                continue;
            }
            if (StringUtils.isEmpty(vehicleType)) {
                vehicleTypeQty++;
                continue;
            }
            if (StringUtils.isEmpty(materialType)) {
                materialTypeQty++;
                continue;
            }
            if (Objects.isNull(enable)) {
                enableQty++;
                continue;
            }
            Material material = materialService.getMaterialByCode(syncMaterialDto.getMaterialCode());
            if (material != null) {
                LambdaUpdateWrapper<Material> updateWrapper = Wrappers.lambdaUpdate();
                updateWrapper.eq(Material::getCode, material.getCode())
                        .set(Material::getName, syncMaterialDto.getMaterialName())
                        .set(Material::getSpec, syncMaterialDto.getMaterialSpec())
                        .set(StringUtils.isNotEmpty(syncMaterialDto.getMaterialUnit()), Material::getSpec, syncMaterialDto.getMaterialUnit())
                        .set(Material::getDescription, syncMaterialDto.getDescription())
                        .set(Material::getVehicleTypeCode, syncMaterialDto.getVehicleType())
                        .set(Material::getType, syncMaterialDto.getMaterialType())
                        .set(Material::getSupplyType, syncMaterialDto.getSupplyType())
                        .set(StringUtils.isNotEmpty(syncMaterialDto.getTracingType()), Material::getTracingType, syncMaterialDto.getTracingType())
                        .set(!Objects.isNull(syncMaterialDto.getWeight()), Material::getWeight, syncMaterialDto.getWeight())
                        .set(!Objects.isNull(syncMaterialDto.getHigh()), Material::getHigh, syncMaterialDto.getHigh())
                        .set(!Objects.isNull(syncMaterialDto.getEnable()), Material::getEnable, syncMaterialDto.getEnable());
                boolean success = materialService.update(updateWrapper);
                if (success) {
                    updateQty++;
                }
            } else {
                Material newMaterial = new Material();
                newMaterial.setCode(syncMaterialDto.getMaterialCode());
                newMaterial.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
                newMaterial.setWarehouseCode(QuantityConstant.DEFAULT_WAREHOUSE);
                newMaterial.setName(syncMaterialDto.getMaterialName());
                newMaterial.setSpec(syncMaterialDto.getMaterialSpec());
                newMaterial.setUnit(syncMaterialDto.getMaterialUnit());
                newMaterial.setSupplyType(syncMaterialDto.getSupplyType());
                newMaterial.setDescription(syncMaterialDto.getDescription());
                newMaterial.setType(syncMaterialDto.getMaterialType());
                newMaterial.setCategory(syncMaterialDto.getCategory());
                newMaterial.setVehicleTypeCode(syncMaterialDto.getVehicleType());
                newMaterial.setEnable(syncMaterialDto.getEnable());
                newMaterial.setCreatedBy(QuantityConstant.PLATFORM_MES);
                newMaterial.setLastUpdatedBy(QuantityConstant.PLATFORM_MES);
                newMaterial.setWeight(syncMaterialDto.getWeight());
                newMaterial.setHigh(syncMaterialDto.getHigh());
                newMaterial.setTracingType(syncMaterialDto.getTracingType());
                boolean success = materialService.save(newMaterial);
                if (success) {
                    saveQty++;
                }
            }
        }
        return AjaxResult.success(StringUtils.format("同步物料数据成功,同步数量:{}个,新增成功:{}个,更新成功:{}个。" +
                        "物料编码为空:{}个,物料名称为空:{}个,物料类别为空:{}个," +
                        "盛具类型为空:{}个,可用状态为空:{}个", syncMaterialDtoList.size(), saveQty, updateQty,
                materialCodeQty, materialNameQty, materialTypeQty, vehicleTypeQty, enableQty));
    }

    @Override
    public AjaxResult syncSupplier(List<SyncSupplierDto> syncSupplierDtoList) {
        int supplierCodeQty = 0, supplierNameQty = 0, enableQty = 0, saveQty = 0, updateQty = 0;
        // 查询该供应商是否已存在:存在则更新,不存在则新增
        for (SyncSupplierDto syncSupplierDto : syncSupplierDtoList) {
            // 数据校验
            String supplierCode = syncSupplierDto.getSupplierCode();
            String supplierName = syncSupplierDto.getSupplierName();
            Boolean enable = syncSupplierDto.getEnable();
            if (StringUtils.isEmpty(supplierCode)) {
                supplierCodeQty++;
                continue;
            }
            if (StringUtils.isEmpty(supplierName)) {
                supplierNameQty++;
                continue;
            }
            if (Objects.isNull(enable)) {
                enableQty++;
                continue;
            }
            Supplier supplier = supplierService.getByCode(syncSupplierDto.getSupplierCode());
            if (supplier != null) {
                LambdaUpdateWrapper<Supplier> updateWrapper = Wrappers.lambdaUpdate();
                updateWrapper.eq(Supplier::getCode, syncSupplierDto.getSupplierCode())
                        .set(Supplier::getName, syncSupplierDto.getSupplierName())
                        .set(StringUtils.isNotEmpty(syncSupplierDto.getState()), Supplier::getState, syncSupplierDto.getState())
                        .set(StringUtils.isNotEmpty(syncSupplierDto.getCity()), Supplier::getCity, syncSupplierDto.getCity())
                        .set(StringUtils.isNotEmpty(syncSupplierDto.getAddress()), Supplier::getAddress1, syncSupplierDto.getAddress())
                        .set(StringUtils.isNotEmpty(syncSupplierDto.getAttentionTo()), Supplier::getAttentionTo, syncSupplierDto.getAttentionTo())
                        .set(StringUtils.isNotEmpty(syncSupplierDto.getPhoneNum()), Supplier::getPhoneNum, syncSupplierDto.getPhoneNum())
                        .set(Supplier::getEnable, syncSupplierDto.getEnable());
                boolean success = supplierService.update(updateWrapper);
                if (!success) {
                    updateQty++;
                }
            } else {
                Supplier newSupplier = new Supplier();
                newSupplier.setCode(syncSupplierDto.getSupplierCode());
                newSupplier.setWarehouseCode(QuantityConstant.DEFAULT_WAREHOUSE);
                newSupplier.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
                newSupplier.setName(syncSupplierDto.getSupplierName());
                newSupplier.setAddress1(syncSupplierDto.getAddress());
                newSupplier.setAddress2("");
                newSupplier.setCity(syncSupplierDto.getCity());
                newSupplier.setState(syncSupplierDto.getState());
                newSupplier.setAttentionTo(syncSupplierDto.getAttentionTo());
                newSupplier.setPhoneNum(syncSupplierDto.getPhoneNum());
                newSupplier.setEnable(syncSupplierDto.getEnable());
                newSupplier.setCreated(new Date());
                newSupplier.setCreatedBy(QuantityConstant.PLATFORM_MES);
                newSupplier.setLastUpdated(new Date());
                newSupplier.setLastUpdatedBy(QuantityConstant.PLATFORM_MES);
                boolean success = supplierService.save(newSupplier);
                if (!success) {
                    saveQty++;
                }
            }
        }
        return AjaxResult.success(StringUtils.format("同步供应商数据成功,同步数量:{},新增成功:{}个,更新成功:{}个。" +
                        "供应商编码为空:{}个,供应商名称为空:{}个,物料类别为空:{}个," +
                        "盛具类型为空:{}个,可用状态为空:{}个", syncSupplierDtoList.size(), saveQty, updateQty,
                supplierCodeQty, supplierNameQty, enableQty));
    }

    @Override
    public AjaxResult syncVehicleType(List<SyncVehicleTypeDto> syncVehicleTypeDtoList) {
        int vehicleTypeCodeQty = 0, vehicleTypeNameQty = 0, maxXQty = 0, maxYQty = 0, enableQty = 0, saveQty = 0, updateQty = 0;
        for (SyncVehicleTypeDto syncVehicleTypeDto : syncVehicleTypeDtoList) {
            // 数据校验
            String vehicleTypeCode = syncVehicleTypeDto.getVehicleTypeCode();
            String vehicleTypeName = syncVehicleTypeDto.getVehicleTypeName();
            Integer maxX = syncVehicleTypeDto.getMaxX();
            Integer maxY = syncVehicleTypeDto.getMaxY();
            Boolean enable = syncVehicleTypeDto.getEnable();
            if (StringUtils.isEmpty(vehicleTypeCode)) {
                vehicleTypeCodeQty++;
            }
            if (StringUtils.isEmpty(vehicleTypeName)) {
                vehicleTypeNameQty++;
            }
            if (Objects.isNull(maxX)) {
                maxXQty++;
            }
            if (Objects.isNull(maxY)) {
                maxYQty++;
            }
            if (Objects.isNull(enable)) {
                enableQty++;
            }
            VehicleType vehicleType = vehicleTypeService.getByCode(syncVehicleTypeDto.getVehicleTypeCode());
            if (vehicleType != null) {
                LambdaUpdateWrapper<VehicleType> updateWrapper = Wrappers.lambdaUpdate();
                updateWrapper.eq(VehicleType::getCode, syncVehicleTypeDto.getVehicleTypeCode())
                        .set(VehicleType::getName, syncVehicleTypeDto.getVehicleTypeName())
                        .set(VehicleType::getMaxX, syncVehicleTypeDto.getMaxX())
                        .set(VehicleType::getMaxY, syncVehicleTypeDto.getMaxY())
                        .set(VehicleType::getEnable, syncVehicleTypeDto.getEnable());
                boolean success = vehicleTypeService.update(updateWrapper);
                if (!success) {
                    updateQty++;
                }
            } else {
                VehicleType newVehicleType = new VehicleType();
                newVehicleType.setCode(syncVehicleTypeDto.getVehicleTypeCode());
                newVehicleType.setName(syncVehicleTypeDto.getVehicleTypeName());
                newVehicleType.setMaxX(syncVehicleTypeDto.getMaxX());
                newVehicleType.setMaxY(syncVehicleTypeDto.getMaxY());
                newVehicleType.setWarehouseCode(QuantityConstant.DEFAULT_WAREHOUSE);
                newVehicleType.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
                newVehicleType.setEnable(syncVehicleTypeDto.getEnable());
                newVehicleType.setCreated(new Date());
                newVehicleType.setCreatedBy(QuantityConstant.PLATFORM_MES);
                newVehicleType.setLastUpdated(new Date());
                newVehicleType.setLastUpdatedBy(QuantityConstant.PLATFORM_MES);
                boolean success = vehicleTypeService.save(newVehicleType);
                if (!success) {
                    saveQty++;
                }
            }
        }
        return AjaxResult.success(StringUtils.format("同步盛具类型数据成功,同步数量:{}个,新增成功:{}个,更新成功:{}个。" +
                        "盛具类型编码为空:{}个,盛具类型名称为空:{}个,行为空:{}个," +
                        "列为空:{}个,可用状态为空:{}个", syncVehicleTypeDtoList.size(), saveQty, updateQty,
                vehicleTypeCodeQty, vehicleTypeNameQty, maxXQty, maxYQty, enableQty));
    }

    @Override
    public AjaxResult<List<EmptyLocationDto>> queryEmptyLocation() {
        List<EmptyLocationDto> emptyLocationDtoList = locationService.queryEmptyLocation();
        return AjaxResult.success(emptyLocationDtoList);
    }

    @Override
    public AjaxResult<?> queryPowder() {
        List<Map<String, Object>> maps = new ArrayList<>();
        List<Zone> zoneList = zoneService.list();
        zoneList.forEach(zone -> {
            List<InventoryDetail> inventoryDetailList = inventoryDetailService.getByZoneCode(zone.getCode());
            double powder = inventoryDetailList.stream().mapToDouble(InventoryDetail::getWeight).sum();
            HashMap<String, Object> map = new HashMap<>();
            map.put("zoneCode", zone.getCode());
            map.put("zoneName", zone.getName());
            map.put("powder", powder);
            maps.add(map);
        });
        return AjaxResult.success(maps);
    }

    @Override
    public AjaxResult<?> querySolidifyTime(QuerySolidifyTimeDto querySolidifyTimeDto) {
        String orderCode = querySolidifyTimeDto.getOrderCode();
        String materialCode = querySolidifyTimeDto.getMaterialCode();
        String flowCode = querySolidifyTimeDto.getFlowCode();
        LambdaQueryWrapper<InventoryDetail> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(StringUtils.isNotEmpty(orderCode), InventoryDetail::getOrderCode, orderCode)
                .eq(StringUtils.isNotEmpty(materialCode), InventoryDetail::getMaterialCode, materialCode)
                .eq(StringUtils.isNotEmpty(flowCode), InventoryDetail::getFlowCode, flowCode);
        List<InventoryDetail> inventoryDetailList = inventoryDetailService.list(queryWrapper);
        if (inventoryDetailList.isEmpty()) {
            return AjaxResult.success(StringUtils.format("根据条件,工单号:【{}】,物料编码:【{}】,工序号:【{}】 未找到库存",
                    orderCode, materialCode, flowCode));
        }
        List<HashMap<String, Object>> returnMapList = new ArrayList<>();
        Map<String, List<InventoryDetail>> map = inventoryDetailList.stream().collect(Collectors.groupingBy(InventoryDetail::getLocationCode));
        for (Map.Entry<String, List<InventoryDetail>> entry : map.entrySet()) {
            HashMap<String, Object> returnMap = new HashMap<>();
            List<InventoryDetail> value = entry.getValue();
            Optional<InventoryDetail> optional = value.stream().findFirst();
            if (optional.isPresent()) {
                InventoryDetail inventoryDetail = optional.get();
                returnMap.put("locationCode", inventoryDetail.getLocationCode());
                returnMap.put("solidifyTime", inventoryDetail.getSolidifyTime());
                returnMap.put("receiptTime", inventoryDetail.getCreated());
                // 此方法获取的是相差多少秒
                long differenceSecond = DateUtils.difference(new Date(), inventoryDetail.getCreated());
                // 转化为小时
                long differenceHour = differenceSecond / (60 * 60);
                // 计算剩余固化时间
                double surplusTime = (inventoryDetail.getSolidifyTime() - differenceHour) < 0 ? 0 : inventoryDetail.getSolidifyTime() - differenceHour;
                returnMap.put("surplusTime", surplusTime);
                returnMapList.add(returnMap);
            }
        }
        return AjaxResult.success(returnMapList);
    }

    @Override
    public AjaxResult<?> productShipmentDto(ProductShipmentDto productShipmentDto) {
        // 1、数据校验
        String shipmentType = productShipmentDto.getShipmentType();
        String referCode = productShipmentDto.getReferCode();
        String orderCode = productShipmentDto.getOrderCode();
        String toPort = productShipmentDto.getToPort();
        List<ProductShipmentDetailDto> productShipmentDetailList = productShipmentDto.getProductShipmentDetailList();
        if (StringUtils.isEmpty(shipmentType)) {
            return AjaxResult.error("出库失败,出库类型为空");
        }
        if (StringUtils.isEmpty(referCode)) {
            return AjaxResult.error("出库失败,上有单号为空");
        }
        if (StringUtils.isEmpty(orderCode)) {
            return AjaxResult.error("出库失败,工单号为空");
        }
        if (StringUtils.isEmpty(orderCode)) {
            return AjaxResult.error("出库失败,出库口为空");
        }
        if (productShipmentDetailList == null || productShipmentDetailList.isEmpty()) {
            return AjaxResult.error("出库失败,出库物料信息为空");
        }
        for (ProductShipmentDetailDto productShipmentDetailDto : productShipmentDetailList) {
            String materialCode = productShipmentDetailDto.getMaterialCode();
            if (StringUtils.isEmpty(materialCode)) {
                return AjaxResult.error("出库失败,物料信息中有物料编码为空");
            }
            Material material = materialService.getByCode(materialCode);
            if (material == null) {
                return AjaxResult.error(StringUtils.format("出库失败,根据物料编码:{} 未找到物料", materialCode));
            }
            BigDecimal qty = productShipmentDetailDto.getQty();
            if (Objects.isNull(qty) || qty.compareTo(BigDecimal.ZERO) <= 0) {
                return AjaxResult.error("出库失败,物料信息中有物料需求数量为空或小于等于0");
            }

            // 2、出库
            String flowCode = productShipmentDetailDto.getFlowCode();
            Integer materialInspectStatus = productShipmentDetailDto.getMaterialInspectStatus();
            String supplierCode = productShipmentDetailDto.getSupplierCode();
            String supplierBatchNumber = productShipmentDetailDto.getSupplierBatchNumber();
            LambdaQueryWrapper<InventoryDetail> inventoryDetailQueryWrapper = Wrappers.lambdaQuery();
            inventoryDetailQueryWrapper
                    .ne(InventoryDetail::getZoneCode, QuantityConstant.ZONE_TYPE_CBG)
                    .eq(StringUtils.isNotEmpty(orderCode), InventoryDetail::getOrderCode, orderCode)
                    .eq(StringUtils.isNotEmpty(flowCode), InventoryDetail::getFlowCode, flowCode)
                    .eq(StringUtils.isNotEmpty(supplierBatchNumber), InventoryDetail::getSupplierBatch, supplierBatchNumber)
                    .eq(StringUtils.isNotEmpty(supplierCode), InventoryDetail::getSupplierCode, supplierCode)
                    .eq(!Objects.isNull(materialInspectStatus), InventoryDetail::getMaterialInspectStatus, materialInspectStatus)
                    .apply("qty>taskQty")
                    .orderByAsc(InventoryDetail::getCreated);
            List<InventoryDetail> inventoryDetailList = inventoryDetailService.list(inventoryDetailQueryWrapper);
            if (inventoryDetailList.isEmpty()) {
                return AjaxResult.error("出库失败,根据出库条件未找到可用库存");
            }
            BigDecimal inventoryQty = inventoryDetailList.stream().map(InventoryDetail::getQty)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            if (inventoryQty.compareTo(qty) < 0) {
                return AjaxResult.error(StringUtils.format("出库失败,根据出库条件找到的可用库存数量不足,当前可用数量:{}", inventoryQty));
            }
            List<String> shipmentContainerCodeList = new LinkedList<>();
            Map<String, List<InventoryDetail>> inventoryMap = inventoryDetailList.stream().collect(Collectors.groupingBy(InventoryDetail::getContainerCode));
            for (Map.Entry<String, List<InventoryDetail>> entry : inventoryMap.entrySet()) {
                List<InventoryDetail> inventoryDetails = entry.getValue();
                BigDecimal shipmentQty = inventoryDetails.stream().map(InventoryDetail::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);
                qty = qty.subtract(shipmentQty);
                shipmentContainerCodeList.add(entry.getKey());
                if (qty.compareTo(BigDecimal.ZERO) <= 0) {
                    break;
                }
            }
            LambdaQueryWrapper<InventoryHeader> inventoryHeaderQueryWrapper = Wrappers.lambdaQuery();
            inventoryHeaderQueryWrapper.in(InventoryHeader::getContainerCode, shipmentContainerCodeList);
            List<InventoryHeader> inventoryHeaderList = inventoryHeaderService.list(inventoryHeaderQueryWrapper);
            BigDecimal shipmentQty = inventoryHeaderList.stream().map(InventoryHeader::getTotalQty).reduce(BigDecimal.ZERO, BigDecimal::add);
            ShipmentHeader shipmentHeader = new ShipmentHeader();
            shipmentHeader.setShipmentType(shipmentType);
            shipmentHeader.setTotalQty(shipmentQty);
            shipmentHeader.setTotalLines(productShipmentDetailList.size());
            // 生成单据头
            String code = shipmentHeaderService.createCode(shipmentHeader.getShipmentType());
            shipmentHeader.setCode(code);
            shipmentHeader.setReferCode(referCode);
            shipmentHeader.setFirstStatus(QuantityConstant.SHIPMENT_HEADER_GROUPDISK);
            shipmentHeader.setLastStatus(QuantityConstant.SHIPMENT_HEADER_GROUPDISK);
            shipmentHeader.setLastUpdatedBy(StringUtils.isNotEmpty(ShiroUtils.getLoginName()) ? ShiroUtils.getLoginName() : QuantityConstant.PLATFORM_MES);
            shipmentHeader.setCreatedBy(StringUtils.isNotEmpty(ShiroUtils.getLoginName()) ? ShiroUtils.getLoginName() : QuantityConstant.PLATFORM_MES);
            shipmentHeader.setWarehouseCode(QuantityConstant.DEFAULT_WAREHOUSE);
            shipmentHeader.setCompanyCode(QuantityConstant.DEFAULT_COMPANY_CODE);
            boolean success = shipmentHeaderService.save(shipmentHeader);
            ShipmentDetail shipmentDetail = this.createShipmentDetail(materialCode, shipmentQty, shipmentHeader);
            // 生成单据明细
            success = shipmentDetailService.save(shipmentDetail);
            if (!success) {
                throw new ServiceException("出库失败,保存出库单明细失败");
            }

            for (InventoryHeader inventoryHeader : inventoryHeaderList) {
                ShipmentContainerHeader shipmentContainerHeader = this.createShipmentContainerHeader(inventoryHeader, toPort, shipmentHeader);
                // 生成配盘头
                success = shipmentContainerHeaderService.save(shipmentContainerHeader);
                if (!success) {
                    throw new ServiceException("出库失败,保存出库配盘头失败");
                }
                List<InventoryDetail> inventoryDetails = inventoryDetailList.stream()
                        .filter(inventoryDetail -> inventoryDetail.getInventoryHeaderId().equals(inventoryHeader.getId()))
                        .collect(Collectors.toList());
                List<ShipmentContainerDetail> shipmentContainerDetailList = this.createShipmentContainerDetail(shipmentContainerHeader, shipmentDetail, inventoryDetails);
                // 生成配盘明细
                success = shipmentContainerDetailService.saveBatch(shipmentContainerDetailList);
                if (!success) {
                    throw new ServiceException("出库失败,保存出库配盘明细败");
                }
                TaskHeader taskHeader = this.createTaskHeader(shipmentContainerHeader, inventoryHeader);
                success = taskHeaderService.save(taskHeader);
                if (!success) {
                    throw new ServiceException("出库失败,保存任务头表失败");
                }
                List<TaskDetail> taskDetailList = this.createTaskDetail(taskHeader, shipmentDetail, shipmentContainerDetailList);
                success = taskDetailService.saveBatch(taskDetailList);
                if (!success) {
                    throw new ServiceException("出库失败,保存任务明细表失败");
                }
                // 锁定载具和库位
                success = containerService.updateStatus(inventoryHeader.getContainerCode(), QuantityConstant.STATUS_CONTAINER_LOCK);
                if (!success) {
                    throw new ServiceException("出库失败,锁定载具失败");
                }
                success = locationService.updateStatus(inventoryHeader.getLocationCode(), QuantityConstant.STATUS_LOCATION_LOCK);
                if (!success) {
                    throw new ServiceException("出库失败,锁定库位失败");
                }
                InventoryHeader updateInventoryHeader = new InventoryHeader();
                updateInventoryHeader.setId(inventoryHeader.getId());
                updateInventoryHeader.setContainerStatus(QuantityConstant.STATUS_CONTAINER_LOCK);
                updateInventoryHeader.setLastUpdatedBy(StringUtils.isNotEmpty(ShiroUtils.getLoginName()) ? ShiroUtils.getLoginName() : QuantityConstant.PLATFORM_MES);
                success = inventoryHeaderService.updateById(updateInventoryHeader);
                if (!success) {
                    throw new ServiceException("出库失败,锁定库存失败");
                }
                // 更新库存任务数量
                List<InventoryDetail> updateInventoryDetailList = new ArrayList<>();
                inventoryDetails.forEach(inventoryDetail -> {
                    InventoryDetail updateInventoryDetail = new InventoryDetail();
                    updateInventoryDetail.setId(inventoryDetail.getId());
                    updateInventoryDetail.setTaskQty(inventoryDetail.getQty());
                    updateInventoryDetailList.add(updateInventoryDetail);
                });
                success = inventoryDetailService.updateBatchById(updateInventoryDetailList);
                if (!success) {
                    throw new ServiceException("出库失败,更新库存占用数量失败");
                }
            }
        }
        return AjaxResult.success("出库成功");
    }
}