package com.prolog.upcloud.base.wh.operate.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.codingapi.tx.annotation.TxTransaction;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Lists;
import com.prolog.cs.basic.chain.model.cargo.BaseCargoOwnerMst;
import com.prolog.cs.basic.common.model.BaseCommonDict;
import com.prolog.cs.basic.item.model.BaseMtlItemMst;
import com.prolog.cs.basic.item.model.BaseMtlItemWms;
import com.prolog.cs.basic.item.model.vo.ItemBaseInfoVO;
import com.prolog.cs.basic.warehouse.model.BaseWmsWarehouseArea;
import com.prolog.cs.basic.warehouse.model.BaseWmsWarehouseLocator;
import com.prolog.cs.basic.warehouse.model.BaseWmsWarehouseMst;
import com.prolog.cs.basic.warehouse.model.dto.WmsWhBldgDTO;
import com.prolog.cs.basic.warehouse.model.vo.ContainerQueryVO;
import com.prolog.cs.basic.warehouse.model.vo.WarehouseLocatorBasicInfo;
import com.prolog.cs.biz.control.model.vo.LocatorQueryVO;
import com.prolog.cs.biz.inbound.constant.BizConstant;
import com.prolog.cs.biz.inbound.dto.WhAreaCategoryContext;
import com.prolog.cs.biz.outbound.model.dto.ContainerConfirmDto;
import com.prolog.cs.biz.outbound.model.dto.EisConfirmSubVo;
import com.prolog.cs.data.exchange.client.ExchangeSender;
import com.prolog.cs.data.exchange.client.core.EtlMessage;
import com.prolog.cs.data.exchange.client.core.EtlTypeConstants;
import com.prolog.cs.platform.power.model.enterprise.UserBasicVO;
import com.prolog.entity.dto.QueryContainerPositionDTO;
import com.prolog.entity.vo.InventoryAuditVO;
import com.prolog.entity.vo.InventoryLockVO;
import com.prolog.entity.vo.QueryContainerPositionVO;
import com.prolog.enums.InventoryAuditEnum;
import com.prolog.enums.InventoryLockEnum;
import com.prolog.framework.authority.core.dto.UserDTO;
import com.prolog.framework.authority.user.UserTemplate;
import com.prolog.framework.bz.common.integration.IntegrationApi;
import com.prolog.framework.bz.common.integration.IntegrationSearchData;
import com.prolog.framework.bz.common.print.PrintData;
import com.prolog.framework.bz.common.print.ReportData;
import com.prolog.framework.bz.common.sequence.SequenceApi;
import com.prolog.framework.bz.core.collection.ValidList;
import com.prolog.framework.bz.core.model.ModelUtils;
import com.prolog.framework.bz.core.util.ObjectUtils;
import com.prolog.framework.common.message.RestMessage;
import com.prolog.framework.core.exception.BizException;
import com.prolog.framework.core.exception.PrologException;
import com.prolog.framework.core.pojo.Page;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.FieldSelector;
import com.prolog.framework.core.restriction.Restriction;
import com.prolog.framework.core.restriction.Restrictions;
import com.prolog.framework.dao.util.PageUtils;
import com.prolog.framework.utils.AssertUtils;
import com.prolog.framework.utils.JsonUtils;
import com.prolog.framework.utils.MapUtils;
import com.prolog.framework.utils.SnowFlakeUtils;
import com.prolog.inte.EisHandler;
import com.prolog.upcloud.base.inventory.dto.DeviceContainerItemStockDTO;
import com.prolog.upcloud.base.inventory.dto.InstockItemInfo;
import com.prolog.upcloud.base.inventory.model.WmsInvStockLot;
import com.prolog.upcloud.base.inventory.vo.StockLotLockVO;
import com.prolog.upcloud.base.inventory.vo.*;
import com.prolog.upcloud.base.strategy.domain.core.Strategy;
import com.prolog.upcloud.base.strategy.dto.MoveRuleDTO;
import com.prolog.upcloud.base.strategy.dto.StrategyDTO;
import com.prolog.upcloud.base.wh.operate.constant.EisBusinessType;
import com.prolog.upcloud.base.wh.operate.constant.EisWorkType;
import com.prolog.upcloud.base.wh.operate.constant.MsgTemplate;
import com.prolog.upcloud.base.wh.operate.constant.StereoHdrStatus;
import com.prolog.upcloud.base.wh.operate.convert.EisConvert;
import com.prolog.upcloud.base.wh.operate.convert.MoveConvert;
import com.prolog.upcloud.base.wh.operate.convert.StockConvert;
import com.prolog.upcloud.base.wh.operate.core.RestMsgHelper;
import com.prolog.upcloud.base.wh.operate.dao.*;
import com.prolog.upcloud.base.wh.operate.enums.*;
import com.prolog.upcloud.base.wh.operate.model.*;
import com.prolog.upcloud.base.wh.operate.resource.*;
import com.prolog.upcloud.base.wh.operate.service.CargoChangeService;
import com.prolog.upcloud.base.wh.operate.service.MoveService;
import com.prolog.upcloud.base.wh.operate.service.MqOperateProducer;
import com.prolog.upcloud.base.wh.operate.service.UserService;
import com.prolog.upcloud.base.wh.operate.utils.*;
import com.prolog.upcloud.base.wh.operate.vo.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.prolog.upcloud.base.wh.operate.constant.BillNoConstant.MOVE_HEADER_ID;
import static com.prolog.upcloud.base.wh.operate.constant.BillNoConstant.STEREO_BILL_NO;
import static com.prolog.upcloud.base.wh.operate.constant.EisConstant.EIS_LOCATION;
import static com.prolog.upcloud.base.wh.operate.constant.EisConstant.YES;
import static com.prolog.upcloud.base.wh.operate.constant.WhOperataConstant.PICK_MODE_DEVICE;
import static com.prolog.upcloud.base.wh.operate.enums.WmsMoveDStatus.WMS_MOVE_OUT_AGV;

@Slf4j
@Service
@RequiredArgsConstructor
public class MoveServiceImpl implements MoveService {

    private final WmsMoveDMapper moveDMapper;

    private final WmsMoveHMapper moveHMapper;

    private final OmsStockAdjustmentResultMapper adjustmentResultMapper;

    private final OmsStockAdjustmentRecordMapper adjustmentRecordMapper;

    private final UserService userService;

    private final StockResource stockResource;

    private final ItemResource itemResource;

    private final WarehouseClient warehouseClient;

    private final ChainResource chainResource;

    private final PlatformPowerClient platformPowerClient;

    private final StrategyClient strategyClient;

    private final ExchangeSender exchangeSender;

    private final GspClient gspClient;

    private final ControlClient controlClient;

    private final UserTemplate userTemplate;

    @Resource
    private CargoChangeService cargoChangeService;

    @Resource
    private CommonResource commonResource;

    @Value("${prolog.wms.areaCategoryDictId:00000000000001}")
    private String areaCategoryDictId;

    @Resource
    private IntegrationApi integrationApi;

    @Value("${prolog.integration.sendAgv:282}")
    private String sendAgv;
    @Value("${prolog.integration.sendWms:291}")
    private String sendWms;
    @Value("${prolog.integration.updateAgvStatus:293}")
    private String updateAgvStatus;

    @Value("${prolog.integration.instockTransforId:284}")
    private String instockTransforId;
    @Value("${prolog.integration.instockCallId:288}")
    private String instockCallId;
    @Value("${prolog.integration.instockCallStatusId:301}")
    private String instockCallStatusId;

    @Value("${prolog.wms.vaildateMoveContainer:true}")
    private Boolean vaildateMoveContainer;

    @Value("${oms.omsStockAdjustmentResultUrl}")
    private String omsStockAdjustmentResultUrl;

    @Resource
    private MoveConvert moveConvert;
    @Resource
    private StockConvert stockConvert;
    @Resource
    private EisConvert eisConvert;
    @Resource
    private WmsStereoDMapper stereoDMapper;
    @Resource
    private WmsStereoHMapper stereoHMapper;
    @Resource
    private MqOperateProducer mqProducer;
    @Resource
    private EisHandler eisHandler;


    /**
     * 生成移库单
     *
     * @param saveVO 移库信息
     */
    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public void saveMoveInfo(WmsMoveSaveVO saveVO) {
        if (vaildateMoveContainer) {
            // 校验容器
            vaildateContainer(saveVO);
        }

        UserDTO user = userService.getUser();

        RestMessage<List<BaseWmsWarehouseMst>> whResult = warehouseClient.getWarehouseBasicInfo(new String[]{saveVO.getWarehouseId()});
        if (!whResult.isSuccess() || whResult.getData() == null || whResult.getData().isEmpty()) {
            throw new BizException("未找到有效的仓库信息");
        }

        // 查询移库规则
        RestMessage<List<MoveRuleDTO>> moveRuleResult = strategyClient.getMoveList(saveVO.getWarehouseId(), null, null);
        if (!moveRuleResult.isSuccess() || CollectionUtil.isEmpty(moveRuleResult.getData())) {
            throw new BizException("获取移库规则失败");
        }

        String[] locatorIds = Stream.concat(saveVO.getMoveDetails().stream().map(WmsMoveDetailVO::getInLocatorId), saveVO.getMoveDetails().stream().map(WmsMoveDetailVO::getOutLocatorId)).distinct().toArray(String[]::new);
        List<WarehouseLocatorBasicInfo> locatorResult = getWarehouseLocationBasicInfo(locatorIds);

        String[] batchIds = saveVO.getMoveDetails().stream().map(WmsMoveDetailVO::getBatchId).distinct().toArray(String[]::new);
        RestMessage<List<BatchNumberListVO>> batchResult = stockResource.getBatchNumberBasicInfo(batchIds);
        if (!batchResult.isSuccess() || CollectionUtil.isEmpty(batchResult.getData())) {
            throw new BizException("获取批次信息失败");
        }

        BaseWmsWarehouseMst warehouse = whResult.getData().get(0);

        // 移库申请单
        WmsMoveH moveH = new WmsMoveH();
        BeanUtils.copyProperties(saveVO, moveH);
        ModelUtils.setBaseInfo(moveH, ModelUtils.OpType.INSERT, user);
        String next = SequenceApi.getInstance().next("wmsMove");
        moveH.setMoveHeaderId(next);
        moveH.setAutoBillNum(next);
        moveH.setDcId(warehouse.getDcId());
        moveH.setMoveBillType(WmsMoveBillType.WMS.name());
        moveH.setSubBillType(WmsMoveSubBillType.MOVE_AREA.getType());
        moveH.setStatus(WmsMoveStatus.NEW.getStatus());
        moveH.setAccessLevel(WmsAccessLevel.ALL.getCode());
        moveH.setMoveDate(DateUtil.date());
        moveH.setPrintCount(0L);
        moveH.setCreatorName(user.getNickname());
        moveH.setModifierName(user.getNickname());
        moveHMapper.save(moveH);

        AtomicLong lineNum = new AtomicLong(1);
        // 申请单明细
        List<WmsMoveD> dList = BeanConvertUtils.convertListTo(saveVO.getMoveDetails(), WmsMoveD::new, (s, t) -> {
            BigDecimal conversionRate = s.getPieceConversionRate();
            if (ObjectUtils.isNull(conversionRate)) {
                conversionRate = s.getCaseConversionRate();
            }
            t.setConversionRate(conversionRate);
            t.setMoveHeaderId(moveH.getMoveHeaderId());
            t.setWarehouseWorkerId(saveVO.getWarehouseWorkerId());
            t.setMoveInBldg(saveVO.getMoveInBldg());
            t.setMoveOutBldg(saveVO.getMoveOutBldg());
            t.setComments(saveVO.getComments());
            t.setQuantity(s.getCaseQuantity());
            t.setPieceQuantity(t.getQuantity().multiply(t.getCaseConversionRate()).add(t.getOddPieceQuantity()));
            t.setLineNum(lineNum.getAndIncrement());
            ModelUtils.setBaseInfo(t, ModelUtils.OpType.INSERT, user);

            BatchNumberListVO batch = batchResult.getData().stream().filter(x -> x.getBatchId().equals(t.getBatchId())).findFirst().orElse(null);
            if (batch == null) {
                throw new BizException("批次信息不存在或已失效");
            }

            t.setBatchNum(batch.getBatchNum());
            t.setManufacturerLotNum(batch.getManufacturerLotNum());
            t.setBatchProductionDate(batch.getBatchProductionDate());
            t.setBatchExpiredDate(batch.getBatchExpiredDate());
            t.setBatchProductionDateShow(batch.getBatchProductionDateShow());
            t.setBatchExpiredDateShow(batch.getBatchExpiredDateShow());

            String pickMode1 = "null";
            WarehouseLocatorBasicInfo inLocator = locatorResult.stream().filter(x -> x.getWarehouseLocatorId().equals(s.getInLocatorId())).findFirst().orElse(null);
            if (inLocator == null) {
                throw new BizException("移入库位不存在或已失效");
            } else {
                pickMode1 = inLocator.getPickMode();
                t.setInPickMode(pickMode1);
            }
            String pickMode2 = "null";
            WarehouseLocatorBasicInfo outLocator = locatorResult.stream().filter(x -> x.getWarehouseLocatorId().equals(s.getOutLocatorId())).findFirst().orElse(null);
            if (outLocator == null) {
                throw new BizException("移出库位不存在或已失效");
            } else {
                pickMode2 = outLocator.getPickMode();
                t.setOutPickMode(pickMode2);
            }
            String outWarehouseAreaCategory = outLocator.getWarehouseAreaCategory();
            if (!StringUtils.isBlank(outWarehouseAreaCategory) && outWarehouseAreaCategory.equals("AGVL")
                    && (StringUtils.isBlank(s.getOutGoodsCaseCode()) || "N/A".equals(s.getOutGoodsCaseCode()))) {
                throw new BizException("移出库位的货位为AGV货架,移出货格号不能为空!");
            }
//            if (!StringUtils.isBlank(outWarehouseAreaCategory) && outWarehouseAreaCategory.equals("ZJK")) {
//                BigDecimal oddPieceQuantity = t.getOddPieceQuantity();
//                BigDecimal quantity = t.getQuantity();
//                if (oddPieceQuantity != null && oddPieceQuantity.compareTo(BigDecimal.ZERO) != 0) {
//                    throw new BizException("移出库位的货位为整件,请检查的自己的移动数量是否是个整件!");
//                }
//            }
            String inWarehouseAreaCategory = inLocator.getWarehouseAreaCategory();
//            if (!StringUtils.isBlank(inWarehouseAreaCategory) && inWarehouseAreaCategory.equals("ZJK")) {
//                BigDecimal oddPieceQuantity = t.getOddPieceQuantity();
//                BigDecimal quantity = t.getQuantity();
//                if (oddPieceQuantity != null && oddPieceQuantity.compareTo(BigDecimal.ZERO) != 0) {
//                    throw new BizException("移入库位的货位为整件,请检查的自己的移动数量是否是个整件!");
//                }
//            }

            // 4.1校验若【移出库别】=【立库】，【移入库别】=【立库】则提示“立库与立库之间不能执行移库操作”;
            // 4.2校验若【移出库别】=【立库】，【移出库存状态】=不合格，【移入库别】必须等于【不合格库】或【残次品库】，若不等于则提示“不合格的立库商品只能移库至残次品库。”
            if (PICK_MODE_DEVICE.equals(t.getOutPickMode()) && "2".equals(t.getOutStockStatus())) {
                if (!StringUtils.isBlank(inWarehouseAreaCategory) && !inWarehouseAreaCategory.equals("NHK")) {
                    throw new BizException("不合格的立库商品只能移库至残次品库！");
                }
            }
//            else if (PICK_MODE_DEVICE.equals(t.getOutPickMode()) && PICK_MODE_DEVICE.equals(t.getInPickMode())) {
//                throw new BizException("立库与立库之间不能执行移库操作！");
//            }

            // t.setAuditComments(pickMode1 + "@" + pickMode2);

            MoveRuleDTO moveRule = moveRuleResult.getData().stream().filter(x -> x.getSrcWarehouseCategory().equals(outLocator.getWarehouseAreaCategory()) && x.getDestWarehouseCategory().equals(inLocator.getWarehouseAreaCategory())).findFirst().orElse(null);
            if (moveRule == null) {
                throw new BizException("未找到有效的移库规则");
            }
            t.setStatus(moveRule.getAuditFlag() == 1 ? WmsMoveDStatus.NEW.getStatus() : WmsMoveDStatus.ERP_AUDIT_PASS.getStatus());
        });
        moveDMapper.saveBatch(dList);

        // 构造移库预占信息
        List<InvStockMove> stockMoves = dList.stream().map(x -> getInvStockMove(x, moveH, locatorResult)).collect(Collectors.toList());
        // 预占
        log.info("创建移库单生成预占：{}",JSONObject.toJSONString(stockMoves));
        RestMessage restMessage = stockResource.moveStock(stockMoves);
        if (!restMessage.isSuccess()) {
            throw new BizException(restMessage.getMessage());
        }

        // 只要是erp审核通过的状态,都需要发送agv下架命令
//        excuteTransferOut(dList, moveH);

    }

    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public List<ReportData> saveMoveInfoBackPrint(WmsMoveSaveVO saveVO) {
        if (vaildateMoveContainer) {
            // 校验容器
            vaildateContainer(saveVO);
        }

        UserDTO user = userService.getUser();

        RestMessage<List<BaseWmsWarehouseMst>> whResult = warehouseClient.getWarehouseBasicInfo(new String[]{saveVO.getWarehouseId()});
        if (!whResult.isSuccess() || whResult.getData() == null || whResult.getData().isEmpty()) {
            throw new BizException("未找到有效的仓库信息");
        }

        // 查询移库规则
        RestMessage<List<MoveRuleDTO>> moveRuleResult = strategyClient.getMoveList(saveVO.getWarehouseId(), null, null);
        if (!moveRuleResult.isSuccess() || CollectionUtil.isEmpty(moveRuleResult.getData())) {
            throw new BizException("获取移库规则失败");
        }

        String[] locatorIds = Stream.concat(saveVO.getMoveDetails().stream().map(WmsMoveDetailVO::getInLocatorId), saveVO.getMoveDetails().stream().map(WmsMoveDetailVO::getOutLocatorId)).distinct().toArray(String[]::new);
        List<WarehouseLocatorBasicInfo> locatorResult = getWarehouseLocationBasicInfo(locatorIds);

        String[] batchIds = saveVO.getMoveDetails().stream().map(WmsMoveDetailVO::getBatchId).distinct().toArray(String[]::new);
        RestMessage<List<BatchNumberListVO>> batchResult = stockResource.getBatchNumberBasicInfo(batchIds);
        if (!batchResult.isSuccess() || CollectionUtil.isEmpty(batchResult.getData())) {
            throw new BizException("获取批次信息失败");
        }

        BaseWmsWarehouseMst warehouse = whResult.getData().get(0);

        // 移库申请单
        WmsMoveH moveH = new WmsMoveH();
        BeanUtils.copyProperties(saveVO, moveH);
        ModelUtils.setBaseInfo(moveH, ModelUtils.OpType.INSERT, user);
        String next = SequenceApi.getInstance().next("wmsMove");
        moveH.setMoveHeaderId(next);
        moveH.setAutoBillNum(next);
        moveH.setDcId(warehouse.getDcId());
        moveH.setMoveBillType(WmsMoveBillType.WMS.name());
        moveH.setSubBillType(WmsMoveSubBillType.MOVE_AREA.getType());
        moveH.setStatus(WmsMoveStatus.NEW.getStatus());
        moveH.setAccessLevel(WmsAccessLevel.ALL.getCode());
        moveH.setMoveDate(DateUtil.date());
        moveH.setPrintCount(0L);
        moveHMapper.save(moveH);

        AtomicLong lineNum = new AtomicLong(1);
        // 申请单明细
        List<WmsMoveD> dList = BeanConvertUtils.convertListTo(saveVO.getMoveDetails(), WmsMoveD::new, (s, t) -> {
            BigDecimal conversionRate = s.getPieceConversionRate();
            if (ObjectUtils.isNull(conversionRate)) {
                conversionRate = s.getCaseConversionRate();
            }
            t.setConversionRate(conversionRate);
            t.setMoveHeaderId(moveH.getMoveHeaderId());
            t.setWarehouseWorkerId(saveVO.getWarehouseWorkerId());
            t.setMoveInBldg(saveVO.getMoveInBldg());
            t.setMoveOutBldg(saveVO.getMoveOutBldg());
            t.setComments(saveVO.getComments());
            t.setQuantity(s.getCaseQuantity());
            t.setPieceQuantity(t.getQuantity().multiply(t.getCaseConversionRate()).add(t.getOddPieceQuantity()));
            t.setLineNum(lineNum.getAndIncrement());
            ModelUtils.setBaseInfo(t, ModelUtils.OpType.INSERT, user);

            BatchNumberListVO batch = batchResult.getData().stream().filter(x -> x.getBatchId().equals(t.getBatchId())).findFirst().orElse(null);
            if (batch == null) {
                throw new BizException("批次信息不存在或已失效");
            }

            t.setBatchNum(batch.getBatchNum());
            t.setManufacturerLotNum(batch.getManufacturerLotNum());
            t.setBatchProductionDate(batch.getBatchProductionDate());
            t.setBatchExpiredDate(batch.getBatchExpiredDate());
            t.setBatchProductionDateShow(batch.getBatchProductionDateShow());
            t.setBatchExpiredDateShow(batch.getBatchExpiredDateShow());

            String pickMode1 = "null";
            WarehouseLocatorBasicInfo inLocator = locatorResult.stream().filter(x -> x.getWarehouseLocatorId().equals(s.getInLocatorId())).findFirst().orElse(null);
            if (inLocator == null) {
                throw new BizException("移入库位不存在或已失效");
            } else {
                pickMode1 = inLocator.getPickMode();
                t.setInPickMode(pickMode1);
            }
            String pickMode2 = "null";
            WarehouseLocatorBasicInfo outLocator = locatorResult.stream().filter(x -> x.getWarehouseLocatorId().equals(s.getOutLocatorId())).findFirst().orElse(null);
            if (outLocator == null) {
                throw new BizException("移出库位不存在或已失效");
            } else {
                pickMode2 = outLocator.getPickMode();
                t.setOutPickMode(pickMode2);
            }
            String outWarehouseAreaCategory = outLocator.getWarehouseAreaCategory();
//            if (!StringUtils.isBlank(outWarehouseAreaCategory) && outWarehouseAreaCategory.equals("ZJK")) {
//                BigDecimal oddPieceQuantity = t.getOddPieceQuantity();
//                BigDecimal quantity = t.getQuantity();
//                if (oddPieceQuantity != null && oddPieceQuantity.compareTo(BigDecimal.ZERO) != 0) {
//                    throw new BizException("移出库位的货位为整件,请检查的自己的移动数量是否是个整件!");
//                }
//            }
            String inWarehouseAreaCategory = inLocator.getWarehouseAreaCategory();
//            if (!StringUtils.isBlank(inWarehouseAreaCategory) && inWarehouseAreaCategory.equals("ZJK")) {
//                BigDecimal oddPieceQuantity = t.getOddPieceQuantity();
//                BigDecimal quantity = t.getQuantity();
//                if (oddPieceQuantity != null && oddPieceQuantity.compareTo(BigDecimal.ZERO) != 0) {
//                    throw new BizException("移入库位的货位为整件,请检查的自己的移动数量是否是个整件!");
//                }
//            }

            // 4.1校验若【移出库别】=【立库】，【移入库别】=【立库】则提示“立库与立库之间不能执行移库操作”;
            //            4.2校验若【移出库别】=【立库】，【移出库存状态】=不合格，【移入库别】必须等于【不合格库】或【残次品库】，若不等于则提示“不合格的立库商品只能移库至残次品库。”
            if (PICK_MODE_DEVICE.equals(t.getOutPickMode()) && "2".equals(t.getOutStockStatus())) {
                if (!StringUtils.isBlank(inWarehouseAreaCategory) && !inWarehouseAreaCategory.equals("NHK")) {
                    throw new BizException("不合格的立库商品只能移库至残次品库！");
                }
            }
            // 要2024-02-29 业务要求立库与立库之间能执行移库操作
//            else if (PICK_MODE_DEVICE.equals(t.getOutPickMode()) && PICK_MODE_DEVICE.equals(t.getInPickMode())) {
//                throw new BizException("立库与立库之间不能执行移库操作！");
//            }

            //			t.setAuditComments(pickMode1 + "@" + pickMode2);

            MoveRuleDTO moveRule = moveRuleResult.getData().stream().filter(x -> x.getSrcWarehouseCategory().equals(outLocator.getWarehouseAreaCategory()) && x.getDestWarehouseCategory().equals(inLocator.getWarehouseAreaCategory())).findFirst().orElse(null);
            if (moveRule == null) {
                throw new BizException("未找到有效的移库规则");
            }
            t.setStatus(moveRule.getAuditFlag() == 1 ? WmsMoveDStatus.NEW.getStatus() : WmsMoveDStatus.ERP_AUDIT_PASS.getStatus());
        });
        moveDMapper.saveBatch(dList);

        // 构造移库预占信息
        List<InvStockMove> stockMoves = dList.stream().map(x -> getInvStockMove(x, moveH, locatorResult)).collect(Collectors.toList());
        // 预占
        RestMessage restMessage = stockResource.moveStock(stockMoves);
        if (!restMessage.isSuccess()) {
            throw new BizException(restMessage.getMessage());
        }
        //查找建筑物
        String[] bldgIds = new String[]{saveVO.getMoveInBldg(),saveVO.getMoveOutBldg()};
        RestMessage<List<WmsWhBldgDTO>> bldgBasicInfoList = warehouseClient.getBldgBasicInfoList(bldgIds);
        if(bldgBasicInfoList==null||!bldgBasicInfoList.isSuccess()){
            throw new BizException("查找建筑物失败");
        }
        List<WmsWhBldgDTO> data = bldgBasicInfoList.getData();
        //查找商品信息
        String[] itemIds = dList.stream().map(x -> x.getItemId()).distinct().toArray(String[]::new);
        RestMessage<List<ItemBaseInfoVO>> itemBaseList = itemResource.getItemBaseList(itemIds);
        if(itemBaseList==null||!itemBaseList.isSuccess()){
            throw new BizException("查找商品信息失败");
        }
        List<ItemBaseInfoVO> itemBaseInfoVOS = itemBaseList.getData();
        WmsWhBldgDTO inBldg = data.stream().filter(x -> x.getBldgId().equals(saveVO.getMoveInBldg())).findFirst().orElse(null);
        WmsWhBldgDTO outBldg = data.stream().filter(x -> x.getBldgId().equals(saveVO.getMoveOutBldg())).findFirst().orElse(null);
        // 只要是erp审核通过的状态,都需要发送agv下架命令
//        excuteTransferOut(dList, moveH);
        List<ReportData> reportDataList = new ArrayList<>();
        ReportData reportData = new ReportData();
        Map<String,Object> head = new HashMap<>();
        head.put("enterpriseName",user.getEnterpriseName());
        head.put("workerNo",user.getUserBizModel().getEmployeeNo());
        head.put("printUser",user.getUserBizModel().getEmployeeName());
        head.put("moveInBldgName",inBldg.getBldgName()!=null?inBldg.getBldgName():"");
        head.put("moveOutBldgName",outBldg.getBldgName()!=null?outBldg.getBldgName():"");
        head.put("moveHeaderId",moveH.getMoveHeaderId());
        head.put("deptName","");

        List<Map<String,Object>> table = new ArrayList<>();
        dList.stream().forEach(x->{
            ItemBaseInfoVO baseInfoVO = itemBaseInfoVOS.stream().filter(it -> it.getItemId().equals(x.getItemId())).findFirst().orElse(null);
            Map<String,Object> map = new HashMap<>();
            if(baseInfoVO!=null){
                map.put("itemCode",baseInfoVO.getItemCode());
                map.put("itemName",baseInfoVO.getItemName());
                map.put("specification",baseInfoVO.getSpecification());
                map.put("conversionRate",baseInfoVO.getCaseConversionRate());
                map.put("uom",baseInfoVO.getCaseUom());
                map.put("manufacturer",baseInfoVO.getManufacturerName());
            }
            WarehouseLocatorBasicInfo inLocatorInfo = locatorResult.stream().filter(lo -> lo.getWarehouseLocatorId().equals(x.getInLocatorId())).findFirst().orElse(null);
            WarehouseLocatorBasicInfo outLocatorInfo = locatorResult.stream().filter(lo -> lo.getWarehouseLocatorId().equals(x.getOutLocatorId())).findFirst().orElse(null);
            map.put("outLocatorCode",outLocatorInfo!=null?outLocatorInfo.getWarehouseLocatorCode():"");
            map.put("inLocatorCode",inLocatorInfo!=null?inLocatorInfo.getWarehouseLocatorCode():"");

            map.put("manufacturerLotNum",x.getManufacturerLotNum());
            map.put("pieceQuantity",x.getPieceQuantity());
            map.put("caseQuantity",x.getQuantity());
            map.put("inContainerCode",x.getInContainerCode());
            table.add(map);
        });
        reportData.setHead(head);
        reportData.setTable(table);
        reportDataList.add(reportData);
        return reportDataList;
    }

    private void vaildateContainer(List<MoveBillCreateVO> value) {
        AssertUtils.isNotEmpty(value, "移库的明细的不可为空!");
        Map<String, Integer> inContainerCodeMap = new HashMap<>();
        Map<String, Integer> outContainerCodeMap = new HashMap<>();
        int row = 0;
        for (MoveBillCreateVO wmsMoveDetailVO : value) {
            row++;
            String inContainerCode = wmsMoveDetailVO.getInContainerCode();
            String outContainerCode = wmsMoveDetailVO.getOutContainerCode();
            if (!StringUtils.isEmpty(inContainerCode) && !StringUtils.isEmpty(outContainerCode) && !inContainerCode.equals("N/A") && !outContainerCode.equals("N/A") && inContainerCode.equals(outContainerCode)) {
                throw new BizException(String.format("移库明细,第[%d]行,上下架容器不可选同样的!", row));
            }
            if (!StringUtils.isEmpty(inContainerCode) && !inContainerCode.equals("N/A") && inContainerCodeMap.containsKey(inContainerCode)) {
                throw new BizException(String.format("移库明细,第[%d]行,上架容器和其他明细行的容器重复!请检查!", row));
            } else if (!StringUtils.isEmpty(inContainerCode)) {
                inContainerCodeMap.put(inContainerCode, 1);
            }
            if (!StringUtils.isEmpty(outContainerCode) && !outContainerCode.equals("N/A") && outContainerCodeMap.containsKey(outContainerCode)) {
                throw new BizException(String.format("移库明细,第[%d]行,下架容器和其他明细行的容器重复!请检查!", row));
            } else if (!StringUtils.isEmpty(outContainerCode)) {
                outContainerCodeMap.put(outContainerCode, 1);
            }
        }
    }

    private void vaildateContainer(WmsMoveSaveVO saveVO) {
        List<WmsMoveDetailVO> moveDetails = saveVO.getMoveDetails();
        AssertUtils.isNotEmpty(moveDetails, "移库的明细的不可为空!");
        Map<String, Integer> inContainerCodeMap = new HashMap<>();
        Map<String, Integer> outContainerCodeMap = new HashMap<>();
        int row = 0;
        for (WmsMoveDetailVO wmsMoveDetailVO : moveDetails) {
            row++;
            String inContainerCode = wmsMoveDetailVO.getInContainerCode();
            String outContainerCode = wmsMoveDetailVO.getOutContainerCode();
            if (!StringUtils.isEmpty(inContainerCode) && !StringUtils.isEmpty(outContainerCode) && !inContainerCode.equals("N/A") && !outContainerCode.equals("N/A") && inContainerCode.equals(outContainerCode)) {
                throw new BizException(String.format("移库明细,第[%d]行,上下架容器不可选同样的!", row));
            }
            if (!StringUtils.isEmpty(inContainerCode) && !inContainerCode.equals("N/A") && inContainerCodeMap.containsKey(inContainerCode)) {
                throw new BizException(String.format("移库明细,第[%d]行,上架容器和其他明细行的容器重复!请检查!", row));
            } else if (!StringUtils.isEmpty(inContainerCode)) {
                inContainerCodeMap.put(inContainerCode, 1);
            }
            if (!StringUtils.isEmpty(outContainerCode) && !outContainerCode.equals("N/A") && outContainerCodeMap.containsKey(outContainerCode)) {
                //                throw new BizException(String.format("移库明细,第[%d]行,下架容器和其他明细行的容器重复!请检查!", row));
            } else if (!StringUtils.isEmpty(outContainerCode)) {
                outContainerCodeMap.put(outContainerCode, 1);
            }
        }
    }

    /**
     * 发送agv命令移库下架
     *
     * @param dList
     * @param moveH
     */
    private void excuteTransferOut(List<WmsMoveD> dList, WmsMoveH moveH) {
        if (!ObjectUtils.isNull(dList)) {
            List<WmsMoveD> collect = dList.stream().filter(p -> !StringUtils.isEmpty(p.getStatus()) && WmsMoveDStatus.ERP_AUDIT_PASS.getStatus().equals(p.getStatus()) && !StringUtils.isEmpty(p.getOutPickMode()) && p.getOutPickMode().toUpperCase().equals(PICK_MODE_DEVICE.toUpperCase())).collect(Collectors.toList());
            if (!ObjectUtils.isNull(collect)) {
                // 执行下架操作(对应设备agv出库表)
                excuteTransferAgvOut(collect, moveH);
            }
        }
    }

    /**
     * 执行传输
     *
     * @param collect
     * @param moveH
     */
    private void excuteTransferAgvOut(List<WmsMoveD> collect, WmsMoveH moveH) {
        Map<String, Object> pickTaskH = covertMoveD2AgvOutMap(collect, moveH);

        IntegrationSearchData sd = new IntegrationSearchData();
        sd.setInterfaceId(sendAgv);
        sd.setData(pickTaskH);
        try {
            String string = JsonUtils.toString(sd);
            log.info("移库下架发送agv指令数据->" + string);
            String searchForJsonStr = integrationApi.searchForJsonStr(sd);
            searchForJsonStr = StringUtils.replaceBlank(searchForJsonStr);
            log.info("移库下架发送agv指令数据,返回数据->" + searchForJsonStr);
            JsonNode parse = JsonUtils.parse(searchForJsonStr);
            if (parse.has("success") && !parse.get("success").asBoolean()) {
                log.error("移库下架执行agv指令失败!原因->" + searchForJsonStr);
                throw new BizException("移库下架执行agv指令失败!原因->" + searchForJsonStr);
            }
        } catch (Exception e) {
            log.error("移库下架发送agv指令失败!原因->" + e.getMessage());
            throw new BizException("移库下架发送agv指令失败!原因->" + e.getMessage());
        }

        // 更新为agv下架状态
        Set<String> collect2 = collect.stream().map(WmsMoveD::getMoveLineId).collect(Collectors.toSet());
        WmsMoveD dEntity = new WmsMoveD();
        dEntity.setStatus(WMS_MOVE_OUT_AGV.getStatus());
        Criteria criteria = Criteria.forClass(WmsMoveD.class);
        criteria.restrictionBuilder().in("moveLineId", collect2.toArray(new String[collect2.size()]));
        criteria.buildRestriction();
        moveDMapper.updateFieldsByCriteria(dEntity, FieldSelector.newInstance().include("status"), criteria);
    }

    /**
     * 移库数据转换agv出库数据
     *
     * @param collect
     * @param moveH
     * @return
     */
    private Map<String, Object> covertMoveD2AgvOutMap(List<WmsMoveD> collect, WmsMoveH moveH) {
        Map<String, Object> HMap = new HashMap<String, Object>();
        Map<String, Object> pickTaskHMap = new HashMap<String, Object>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date now = new Date();
        String format = sdf.format(now);
        String DJBH = moveH.getMoveHeaderId(); // 订单编号
        String DWID = moveH.getCargoOwnerId(); // 单位id
        String RQ = format; // 日期
        String RY_KPY = moveH.getModifierName(); // 开票员
        String THFS = "移库出"; // 提货方式
        String BMID = ""; // 部门id
        String CKYXJ = "1"; // 出库优先级
        String KPSJ = format; // 开票时间
        String BCH = ""; // 波次号
        String KHBZ = ""; // 客户备注
        String DDLX = "1"; // 订单类型1 移库出
        String YZID = moveH.getCargoOwnerId(); // 业主id
        pickTaskHMap.put("DJBH", DJBH);
        pickTaskHMap.put("DWID", DWID);
        pickTaskHMap.put("RQ", RQ);
        pickTaskHMap.put("RY_KPY", RY_KPY);
        pickTaskHMap.put("THFS", THFS);
        pickTaskHMap.put("BMID", BMID);
        pickTaskHMap.put("CKYXJ", CKYXJ);
        pickTaskHMap.put("KPSJ", KPSJ);
        pickTaskHMap.put("BCH", BCH);
        pickTaskHMap.put("KHBZ", KHBZ);
        pickTaskHMap.put("DDLX", DDLX);
        pickTaskHMap.put("YZID", YZID);
        List<Map<String, Object>> pickTaskDList = new ArrayList<>();
        int row = 0;
        boolean anyMatch = collect.stream().anyMatch(p -> StringUtils.isBlank(p.getManufacturerLotNum()));
        Set<String> collect2 = collect.stream().map(p -> p.getMoveLineId()).collect(Collectors.toSet());
        if (anyMatch) {
            log.info("明细中有批号为空的,调用sql再查询一次");
            collect = this.moveDMapper.findByIds(WmsMoveD.class, collect2.toArray(), null);
        }
        for (WmsMoveD wmsMoveD : collect) {
            row++;
            Map<String, Object> pickTaskD = new HashMap<>();
            String itemId = wmsMoveD.getItemId();
            BigDecimal quantity = wmsMoveD.getQuantity(); // 整件数
            BigDecimal pieceQuantity = wmsMoveD.getPieceQuantity(); // 总数
            BigDecimal oddPieceQuantity = wmsMoveD.getOddPieceQuantity(); // 零散
            String manufacturerLotNum = wmsMoveD.getManufacturerLotNum();
            String comments = wmsMoveD.getComments();
            log.info(String.format("行号[%d],批号[%s]", row, manufacturerLotNum));
            pickTaskD.put("DJBH", DJBH); // 订单编号
            pickTaskD.put("DJ_SORT", row); // 明细行号
            pickTaskD.put("SPID", itemId); // 商品id
            pickTaskD.put("SL", pieceQuantity);
            pickTaskD.put("JS", quantity);
            pickTaskD.put("LSS", oddPieceQuantity);
            pickTaskD.put("PH", manufacturerLotNum);
            pickTaskD.put("YZID", YZID);
            pickTaskD.put("MX_BZ", comments); // 明细备注
            pickTaskD.put("ROWNO_BILL", wmsMoveD.getMoveLineId()); // 原行号
            pickTaskD.put("SL_JH", pieceQuantity); // 计划数量
            pickTaskD.put("SL_LHWCL", oddPieceQuantity); // 零货未处理数量
            pickTaskDList.add(pickTaskD);
        }
        pickTaskHMap.put("pickTaskD", pickTaskDList);
        HMap.put("pickTaskH", pickTaskHMap);
        return HMap;
    }

    /**
     * pda移位
     *
     * @param vo pda移位信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @TxTransaction(isStart = true)
    public void saveMoveLocatorInfoPda(WmsMovePdaSaveVO vo) {

        SaveMoveLocatorVO location = check4SaveMoveLocatorInfoPdaAndGetLocationInfo(vo);

        UserDTO user = userService.getUser();

        final String hrdId = IdUtil.getId(MOVE_HEADER_ID);

        List<@Valid WmsMovePdaDetailVO> details = vo.getDetails();

        String[] batchIds = details.stream().map(WmsMovePdaDetailVO::getBatchId).distinct().toArray(String[]::new);

        List<BatchNumberListVO> batchs = RestMsgHelper.unpack(stockResource.getBatchNumberBasicInfo(batchIds));

        List<WmsMoveD> dtls = checkAndGetWmsMoveDS(vo, location, user, hrdId, details, batchs);

        WmsMoveH hdr = moveConvert.generateMoveHdr4saveMoveLocatorInfoByPda(vo, user, location.getInLocator(), hrdId, batchs.get(0).getCargoOwnerId());

        moveHMapper.save(hdr);

        moveDMapper.saveBatch(dtls);

        stockHandle4SaveMoveLocatorInfo(hdr, dtls);
    }

    private List<WmsMoveD> checkAndGetWmsMoveDS(WmsMovePdaSaveVO vo, SaveMoveLocatorVO location, UserDTO user, String hrdId, List<@Valid WmsMovePdaDetailVO> details, List<BatchNumberListVO> batchs) {

        List<WmsMoveD> result = new ArrayList<>(details.size());

        // Step1: 远程找寻策略
//        String enterpriseId = user.getEnterpriseId();
        String warehouseId = vo.getWarehouseId();
        String ownerId = vo.getCargoOwnerId();

//        Strategy strategy = getStrategy(enterpriseId, warehouseId, ownerId);

        for (WmsMovePdaDetailVO detailVO : details) {
            BatchNumberListVO batch = batchs.stream().filter(b -> b.getBatchId().equals(detailVO.getBatchId())).findFirst().orElse(null);
            Assert.notNull(batch, "批次:{}不存在", detailVO.getBatchId());
            WmsMoveD dtl = moveConvert.generateMoveDtl4saveMoveLocatorInfo4Pda(vo, detailVO, user, location.getInLocator(), location.getOutLocator(), hrdId, batch);

            //checkPutawayStrategy(strategy, dtl);

            checkLocatorLimit(dtl.getItemId(), warehouseId, ownerId, "", dtl.getInLocatorId());

            result.add(dtl);
        }
        return result;
    }

    private void checkPutawayStrategy(Strategy strategy, WmsMoveD dtl) {
        WhAreaCategoryContext context = new WhAreaCategoryContext();
        WhAreaCategoryContext.RcvLineData rcvLineData = context.new RcvLineData();
        rcvLineData.setBillType("4");
        rcvLineData.setStorageStatus(dtl.getInStockStatus());
        String isCase = getCaseOrPiece(dtl.getPieceQuantity(), dtl.getConversionRate());
        if (!com.prolog.framework.utils.StringUtils.isBlank(isCase)) {
            rcvLineData.setIsCase(isCase);
            if ("0".equals(isCase)) {
                BigDecimal oddCaseQty = dtl.getCaseConversionRate().divide(dtl.getConversionRate(), 0, RoundingMode.DOWN);
                rcvLineData.setQty(oddCaseQty.doubleValue());
            }
        }
        BaseMtlItemMst item = RestMsgHelper.unpack(itemResource.item(dtl.getItemId()));
        Optional.ofNullable(item).map(BaseMtlItemMst::getBaseMtlItemWms).map(BaseMtlItemWms::getItemIsSpecialSharped).ifPresent(i -> rcvLineData.setStoreTemperature(i.toString()));
        try {
            context.setRcvLineData(rcvLineData);
            log.info(String.format("上架策略执行参数[%s]", JsonUtils.toString(context)));
            context.setTargetAreaCategoryCodes(com.google.common.collect.Lists.newArrayList());
            strategy.execute(context);
        } catch (Exception e) {
            log.error("获取上架库别策略-策略执行失败->", e);
        }
        boolean completed = context.completed();
        Assert.isFalse(!completed, String.format("获取入库库别策略-策略执行失败发生返回失败【%s】", JSONObject.toJSONString(dtl)));
        // 找到匹配的策略
        List<String> targetAreaCategoryCodes = context.getTargetAreaCategoryCodes();

        Assert.isFalse(targetAreaCategoryCodes == null || targetAreaCategoryCodes.size() == 0, String.format("获取入库库别策略-策略执行未匹配到库别【%s】", JSONObject.toJSONString(dtl)));
    }

    private void checkLocatorLimit(String itemId, String warehouseId, String ownerId, String warehouseAreaCategory, String dtl1) {
//        Map<String, Object> paramsVO = new HashMap<>();
//        paramsVO.put("enterpriseId", enterpriseId);
//        paramsVO.put("warehouseId", warehouseId);
//        paramsVO.put("cargoOwnerId", ownerId);
//        //paramsVO.put("areaIds", areaIds);
//        //paramsVO.put("locatorTypes", locatorTypes);
//        //paramsVO.put("itemId", dtl);
//        paramsVO.put("warehouseAreaCategory", warehouseAreaCategory);

        //List<StockLocatorDTO> limitResult = RestMsgHelper.unpack(stockResource.getLocatorIdLimit(paramsVO));
        RestMessage<Page<WarehouseLocatorBasicInfo>> inboundLocatorLimit = controlClient.getInboundLocatorLimit(itemId, ownerId, warehouseId, warehouseAreaCategory, null, null);
        if (inboundLocatorLimit.getData() == null || !inboundLocatorLimit.isSuccess()) {
            throw new PrologException("未找到有效推荐库别的策略,请联系管理员设置!");
        }
        List<WarehouseLocatorBasicInfo> limitResult = inboundLocatorLimit.getData().getList();
        log.info("limitResult:{}", JSONObject.toJSONString(inboundLocatorLimit.getData()));

        long count = limitResult.stream().filter(t -> t.getWarehouseLocatorId().equals(dtl1)).count();
        if (count <= 0) {
            throw new PrologException("当前上架货位不符合上架策略及货位限定约束的可上架货位");
        }
    }

    /**
     * 校验前置逻辑
     */
    private SaveMoveLocatorVO check4SaveMoveLocatorInfoPdaAndGetLocationInfo(WmsMovePdaSaveVO move) {

        String[] locatorCodes = new String[]{move.getInLocatorCode(), move.getOutLocatorCode()};
        List<WarehouseLocatorBasicInfo> locations = RestMsgHelper.unpack(warehouseClient.getLocatorBasicListByCode(move.getWarehouseId(), locatorCodes));
        Assert.notEmpty(locations, "货位信息不存在");

        WarehouseLocatorBasicInfo inLocator = locations.stream().filter(x -> x.getWarehouseLocatorCode().equals(move.getInLocatorCode())).findFirst().orElse(null);
        Assert.notNull(inLocator, "上架货位不存在或已失效");
        WarehouseLocatorBasicInfo outLocator = locations.stream().filter(x -> x.getWarehouseLocatorCode().equals(move.getOutLocatorCode())).findFirst().orElse(null);
        Assert.notNull(outLocator, "下架货位不存在或已失效");

        if (StrUtil.isEmpty(inLocator.getWarehouseAreaCategory())) {
            throw new BizException("上架库别不存在，请检查数据完整性");
        }
        if (!inLocator.getWarehouseAreaCategory().equals(outLocator.getWarehouseAreaCategory())) {
            throw new BizException("移位操作只能在同库别内操作");
        }

        List<BaseWmsWarehouseArea> areas = RestMsgHelper.unpack(warehouseClient.getByAreaCodes(inLocator.getWarehouseId(), new String[]{inLocator.getWarehouseAreaCode(), outLocator.getWarehouseAreaCode()}));
        Assert.notEmpty(areas, "获取库区信息失败");

        boolean isDevice = areas.stream().anyMatch(a -> PICK_MODE_DEVICE.equals(a.getPickMode()));
        Assert.isFalse(isDevice, "立库不能进行货位调整操作");

        String inPickMode = inLocator.getPickMode();
        String outPickMode = outLocator.getPickMode();
        if (StrUtil.isNotBlank(outPickMode) && StrUtil.isNotBlank(inPickMode) && !inPickMode.equals(outPickMode)) {
            throw new BizException("拣货方式不一致!请检查!");
        }

        SaveMoveLocatorVO result = new SaveMoveLocatorVO();
        result.setInLocator(moveConvert.copyWarehouseLocatorBasicInfo(inLocator));
        result.setOutLocator(moveConvert.copyWarehouseLocatorBasicInfo(outLocator));
        return result;
    }

    /**
     * 生成移位申请
     *
     * @param vo 移位
     */
    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public void saveMoveLocatorInfo(WmsMoveSaveVO vo) {

        List<@Valid WmsMoveDetailVO> list = vo.getMoveDetails();

        boolean isSameWarehouseAreaCategory = list.stream().allMatch(t -> t.getInwarehouseAreaCategory().equals(t.getOutwarehouseAreaCategory()));
        Assert.isTrue(isSameWarehouseAreaCategory, "移位操作只能在同库别内操作");

        String[] locatorIds = Stream.concat(vo.getMoveDetails().stream().map(WmsMoveDetailVO::getInLocatorId), vo.getMoveDetails().stream().map(WmsMoveDetailVO::getOutLocatorId)).distinct().toArray(String[]::new);
        List<WarehouseLocatorBasicInfo> locations = RestMsgHelper.unpack(warehouseClient.getLocationBasicInfos(locatorIds));
        Assert.notEmpty(locations, "获取货位信息失败");

        boolean isDevice = locations.stream().anyMatch(t -> PICK_MODE_DEVICE.equals(t.getPickMode()));
        Assert.isFalse(isDevice, "立库不能进行货位调整操作");

        checkLocationLimit(vo);

        UserDTO user = userService.getUser();

        List<BaseWmsWarehouseMst> warehouses = RestMsgHelper.unpack(warehouseClient.getWarehouseBasicInfo(new String[]{vo.getWarehouseId()}));

        String snowId = SequenceApi.getInstance().next("wmsMove");

        WmsMoveH moveH = moveConvert.generateMoveHdr4saveMoveLocatorInfo(vo, user, warehouses.get(0), snowId);

        List<WmsMoveD> dList = checkAndGetWmsMoveDS(vo, user, locations, moveH);

        moveHMapper.save(moveH);

        moveDMapper.saveBatch(dList);

        stockHandle4SaveMoveLocatorInfo(moveH, dList);
    }


    /**
     * 校验货位限定
     */
    public void checkLocationLimit(WmsMoveSaveVO vo) {
        List<@Valid WmsMoveDetailVO> list = vo.getMoveDetails();
        UserDTO user = userService.getUser();
        // Step1: 远程找寻策略
        String enterpriseId = user.getEnterpriseId();
        String warehouseId = vo.getWarehouseId();
        String ownerId = vo.getCargoOwnerId();

        //Strategy strategy = getStrategy(enterpriseId, warehouseId, ownerId);
        Set<String> itemIds = list.stream().map(WmsMoveDetailVO::getItemId).collect(Collectors.toSet());
        List<BaseMtlItemMst> items = RestMsgHelper.unpack(itemResource.getListByIds(itemIds));

        for (WmsMoveDetailVO dtl : list) {
            //checkPutawayStrategy(strategy, items, dtl);

            checkLocatorLimit(dtl.getItemId(), warehouseId, ownerId, dtl.getOutwarehouseAreaCategory(), dtl.getInLocatorId());
        }
    }

    private void checkPutawayStrategy(Strategy strategy, List<BaseMtlItemMst> items, WmsMoveDetailVO dtl) {
        WhAreaCategoryContext context = new WhAreaCategoryContext();
        WhAreaCategoryContext.RcvLineData rcvLineData = context.new RcvLineData();
        rcvLineData.setBillType("4");
        rcvLineData.setStorageStatus(dtl.getInStockStatus());
        String isCase = getCaseOrPiece(dtl.getPieceConversionRate(), dtl.getConversionRate());
        if (!com.prolog.framework.utils.StringUtils.isBlank(isCase)) {
            rcvLineData.setIsCase(isCase);
            if ("0".equals(isCase)) {
                BigDecimal oddCaseQty = dtl.getPieceConversionRate().divide(dtl.getConversionRate(), 0, RoundingMode.DOWN);
                rcvLineData.setQty(oddCaseQty.doubleValue());
            }
        }
        BaseMtlItemMst item = items.stream().filter(i -> dtl.getItemId().equals(i.getItemId())).findFirst().orElse(null);
        Optional.ofNullable(item).map(BaseMtlItemMst::getBaseMtlItemWms).map(BaseMtlItemWms::getItemIsSpecialSharped).ifPresent(i -> rcvLineData.setStoreTemperature(i.toString()));
        try {
            context.setRcvLineData(rcvLineData);
            log.info(String.format("上架策略执行参数[%s]", JsonUtils.toString(context)));
            context.setTargetAreaCategoryCodes(com.google.common.collect.Lists.newArrayList());
            strategy.execute(context);
        } catch (Exception e) {
            log.error("获取上架库别策略-策略执行失败->", e);
        }
        boolean completed = context.completed();
        Assert.isFalse(!completed, String.format("获取入库库别策略-策略执行失败发生返回失败【%s】", JSONObject.toJSONString(dtl)));
        // 找到匹配的策略
        List<String> targetAreaCategoryCodes = context.getTargetAreaCategoryCodes();

        Assert.isFalse(targetAreaCategoryCodes == null || targetAreaCategoryCodes.size() == 0, String.format("获取入库库别策略-策略执行未匹配到库别【%s】", JSONObject.toJSONString(dtl)));
    }

    private String getCaseOrPiece(BigDecimal pieceQuantity, BigDecimal conversionRate) {
        // 是否整散
        if (pieceQuantity != null) {
            BigDecimal oddCaseQty = pieceQuantity.divide(conversionRate, 0, RoundingMode.DOWN);
            BigDecimal oddPieceQty = pieceQuantity.subtract(oddCaseQty.multiply(conversionRate));
            // 判断数据的整散
            if (null != oddPieceQty && oddPieceQty.compareTo(BigDecimal.ZERO) == 0) {
                // 为整数
                return "0";
            }

            if (null != oddCaseQty && oddCaseQty.compareTo(BigDecimal.ZERO) == 0 && null != oddPieceQty && oddPieceQty.compareTo(BigDecimal.ZERO) == 1) {
                // 零散散
                return "1";
            }

            if (null != oddCaseQty && oddCaseQty.compareTo(BigDecimal.ZERO) == 1 && null != oddPieceQty && oddPieceQty.compareTo(BigDecimal.ZERO) == 1) {
                // 整散
                return "2";
            }
            return "0";
        }
        return null;
    }

    private Strategy getStrategy(String enterpriseId, String warehouseId, String ownerId) {
        log.info("-------调用策略服务");
        StrategyDTO strategyDTO = RestMsgHelper.unpack(strategyClient.getStrategyDTO(enterpriseId, "InStock", ownerId, warehouseId));
        Assert.notNull(strategyDTO, "未找到有效推荐库别的策略,请联系相关人员检查自己的策略!");
        Strategy strategy;
        try {
            log.info("创建上架寻库别策略中-------+" + strategyDTO.toString());
            strategy = strategyDTO.createStrategy();
        } catch (Exception e) {
            log.error("策略创建失败,原因->", e);
            throw new BizException("未找到有效推荐库别的策略,请联系管理员设置!");
        }
        log.info("-------调用策略服务成功!");
        return strategy;
    }


    /**
     * 库存处理
     */
    private void stockHandle4SaveMoveLocatorInfo(WmsMoveH moveH, List<WmsMoveD> dList) {
        List<InvStockMove> params = dList.stream().map(x -> stockConvert.generateStockMove(x, moveH)).collect(Collectors.toList());
        log.info("moveLocatorStock:{}", JSON.toJSONString(params));
        RestMsgHelper.unpack(stockResource.moveLocatorStock(params));
    }

    private List<WmsMoveD> checkAndGetWmsMoveDS(WmsMoveSaveVO saveVO, UserDTO user, List<WarehouseLocatorBasicInfo> locatorResult, WmsMoveH moveH) {
        List<@Valid WmsMoveDetailVO> list = saveVO.getMoveDetails();
        return list.stream().map(d -> moveConvert.generateMoveDtl4saveMoveLocatorInfo(d, saveVO, user, locatorResult, moveH)).collect(Collectors.toList());
    }


    private List<WarehouseLocatorBasicInfo> getWarehouseLocationBasicInfo(String[] locatorIds) {
        RestMessage<List<WarehouseLocatorBasicInfo>> msg = warehouseClient.getLocationBasicInfos(locatorIds);
        return RestMsgHelper.unpack(msg);
    }

    /**
     * 审批拒绝移库申请
     *
     * @param lineBillIds 移库单子单编码
     */
    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public void auditRejectMove(String[] lineBillIds) {
        UserDTO user = userService.getUser();

        Criteria criteriaD = Criteria.forClass(WmsMoveD.class);
        criteriaD.restrictionBuilder().eq("enterpriseId", user.getEnterpriseId()).eq("status", WmsMoveDStatus.NEW.getStatus()).in("moveLineId", lineBillIds);
        criteriaD.buildRestriction();

        // 查询需要撤销的单号
        List<WmsMoveD> dList = moveDMapper.findByCriteria(criteriaD);

        // 根据货主设置 ERP 下传的移库是否可以驳回 0-不能驳回(默认)，1-可以驳回 - by Huanglh 2022-02-09
        // 当前的明细归属同一货主(货主是必选项)
        if (ObjectUtils.isNotEmpty(dList)) {
            WmsMoveH moveH = moveHMapper.findById(dList.get(0).getMoveHeaderId(), WmsMoveH.class);
            // 必须是 ERP 下传的移库
            if (ObjectUtils.isNotNull(moveH) && "ERP".equalsIgnoreCase(moveH.getMoveBillType())) {
                RestMessage<Map<String, BaseCargoOwnerMst>> restMessage = chainResource.getCargoOwerByIds(new HashSet<>(Arrays.asList(moveH.getCargoOwnerId())));
                if (!restMessage.isSuccess()) {
                    throw new BizException("货主信息获取失败");
                }

                Map<String, BaseCargoOwnerMst> mstMap = restMessage.getData();
                if (com.prolog.framework.utils.ObjectUtils.isNotNull(mstMap)) {
                    BaseCargoOwnerMst ownerMst = mstMap.values().stream().collect(Collectors.toList()).get(0);
                    if ("0".equals(String.valueOf(ownerMst.getIsErpMwhReject()))) {
                        throw new PrologException("货主已设置 ERP 下传移库不可驳回");
                    }
                }
            }
        }

        String[] cancel = dList.stream().map(WmsMoveD::getMoveLineId).toArray(String[]::new);
        if (cancel.length == 0) {
            throw new BizException("未找到待审核的移库单信息");
        }

        // 审批拒绝子单
        WmsMoveD entity = new WmsMoveD();
        entity.setStatus(WmsMoveDStatus.ERP_AUDIT_REJECT.getStatus());
        ModelUtils.setBaseInfo(entity, ModelUtils.OpType.UPDATE, user);
        String[] field = new String[]{"status", "gmtModified", "modifier", "modifierName"};
        moveDMapper.updateFieldsByCriteria(entity, FieldSelector.newInstance().include(field), criteriaD);

        // 取消预占
        InvStockCancel stockCancel = new InvStockCancel();
        stockCancel.setLineBillIds(cancel);
        RestMessage restMessage = stockResource.moveStockCancel(stockCancel);
        if (!restMessage.isSuccess()) {
            throw new BizException(restMessage.getMessage());
        }
    }

    /**
     * 审批通过移库申请
     *
     * @param auditParamsVO 移库单信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @TxTransaction(isStart = true)
    public void auditMove(MoveAuditParamsVO auditParamsVO) {
        UserDTO user = userService.getUser();

        // 查询明细
        MoveQueryDVO queryVO = new MoveQueryDVO();
        queryVO.setMoveLineIds(auditParamsVO.getLineBillId());
        queryVO.setStatusList(new String[]{WmsMoveDStatus.NEW.getStatus()});
        List<MoveDListVO> dList = moveDMapper.selectMoveDInfoList(user.getEnterpriseId(), queryVO);
        if (CollectionUtil.isEmpty(dList)) {
            throw new BizException("未找到待审核的移库单信息");
        }

        String[] headerIds = dList.stream().map(MoveDListVO::getMoveHeaderId).distinct().toArray(String[]::new);
        Criteria criteriaH = Criteria.forClass(WmsMoveH.class);
        criteriaH.restrictionBuilder().eq("enterpriseId", user.getEnterpriseId()).in("moveHeaderId", headerIds);
        criteriaH.buildRestriction();
        List<WmsMoveH> hList = moveHMapper.findByCriteria(criteriaH);

        // 获取库位信息
        String[] locatorIds = Stream.concat(dList.stream().map(MoveDListVO::getInLocatorId), dList.stream().map(MoveDListVO::getOutLocatorId)).distinct().toArray(String[]::new);
        RestMessage<List<WarehouseLocatorBasicInfo>> locatorResult = warehouseClient.getLocationBasicInfos(locatorIds);
        if (!locatorResult.isSuccess() || CollectionUtil.isEmpty(locatorResult.getData())) {
            throw new BizException("获取库位信息失败");
        }

        Set<String> cargoOwnerIds = dList.stream().map(MoveDListVO::getCargoOwnerId).collect(Collectors.toSet());
        RestMessage<Map<String, BaseCargoOwnerMst>> cargoOwnerResult = chainResource.getCargoOwerByIds(cargoOwnerIds);
//        // 盘点审核信息推送至EIS入参
//        List<InventoryAuditVO> toEisVos = new ArrayList<>();
//
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); // 精确到毫秒
//        List<LockToErpVo> backErpList = new ArrayList<>();
        // 仓库分组
        Map<String, List<MoveDListVO>> whMoveList = dList.stream().collect(Collectors.groupingBy(MoveDListVO::getWarehouseId));
        for (Map.Entry<String, List<MoveDListVO>> moveEntry : whMoveList.entrySet()) {

            // 根据仓库查询移库规则
            RestMessage<List<MoveRuleDTO>> moveRuleResult = strategyClient.getMoveList(moveEntry.getKey(), null, null);
            if (!moveRuleResult.isSuccess() || CollectionUtil.isEmpty(moveRuleResult.getData())) {
                throw new BizException("获取移库规则失败");
            }

            List<MoveDListVO> whDetailList = moveEntry.getValue();
            for (MoveDListVO dInfo : whDetailList) {

                WmsMoveH hInfo = hList.stream().filter(x -> x.getMoveHeaderId().equals(dInfo.getMoveHeaderId())).findFirst().orElse(null);
                if (hInfo == null) {
                    throw new BizException("未找到有效的移库单信息");
                }

                WarehouseLocatorBasicInfo inLocator = locatorResult.getData().stream().filter(x -> x.getWarehouseLocatorId().equals(dInfo.getInLocatorId())).findFirst().orElse(null);
                if (inLocator == null) {
                    throw new BizException("移入库位不存在或已失效");
                } else {
                    String pickMode1 = inLocator.getPickMode();
                    dInfo.setInPickMode(pickMode1);
                    dInfo.setInLocatorCode(inLocator.getWarehouseLocatorCode());
                    dInfo.setInWarehouseAreaCategory(inLocator.getWarehouseAreaCategory());
                    dInfo.setTargetlocation("F" + inLocator.getFloorNum());
                }

                WarehouseLocatorBasicInfo outLocator = locatorResult.getData().stream().filter(x -> x.getWarehouseLocatorId().equals(dInfo.getOutLocatorId())).findFirst().orElse(null);
                if (outLocator == null) {
                    throw new BizException("移出库位不存在或已失效");
                } else {
                    String pickMode2 = outLocator.getPickMode();
                    dInfo.setOutPickMode(pickMode2);
                    dInfo.setOutLocatorCode(outLocator.getWarehouseLocatorCode());
                }
                // 查询商品资料
                RestMessage<BaseMtlItemMst> rest = itemResource.item(dInfo.getItemId());
                if (!rest.isSuccess())
                    throw new RuntimeException("查询商品服务失败！" + rest.getMessage());
                BaseMtlItemMst itemMst = rest.getData();
                dInfo.setItemCode(itemMst.getItemCode());
                MoveRuleDTO moveRule = moveRuleResult.getData().stream().filter(x -> x.getSrcWarehouseCategory().equals(outLocator.getWarehouseAreaCategory()) && x.getDestWarehouseCategory().equals(inLocator.getWarehouseAreaCategory())).findFirst().orElse(null);
                if (moveRule == null) {
                    throw new BizException("未找到有效的移库规则");
                }

                BaseCargoOwnerMst cargoOwnerMst = cargoOwnerResult.getData().get(dInfo.getCargoOwnerId());
                if (cargoOwnerMst == null) {
                    throw new BizException("未找到有效的货主信息");
                }

                Map<String, Object> modifyMap = MapUtil.newHashMap();
                modifyMap.put("audit_comments", auditParamsVO.getAuditComments());
                modifyMap.put("status", WmsMoveDStatus.WMS_AUDIT_PASS.getStatus());
                dInfo.setStatus(WmsMoveDStatus.WMS_AUDIT_PASS.getStatus());
                //				}
                modifyMap.put("gmtModified", DateUtil.date());
                modifyMap.put("modifier", user.getId());
                modifyMap.put("modifierName", user.getUsername());
                moveDMapper.updateMapById(dInfo.getMoveLineId(), modifyMap, WmsMoveD.class);
                // 若选择审核的移库记录中【移出库别】=【设备库】，则推送盘点审核信息至EIS
//                if (PICK_MODE_DEVICE.equals(dInfo.getOutPickMode())) {
//                    InventoryAuditVO toEisVo = new InventoryAuditVO();
//                    toEisVo.setMsgId(cn.hutool.core.util.IdUtil.fastSimpleUUID());
//                    toEisVo.setTaskLineId(dInfo.getMoveLineId());
//                    toEisVo.setWarehouseId(outLocator.getWarehouseId());
//                    toEisVo.setWorkType(InventoryAuditEnum.INV_MOVE.getWorkType());
//                    toEisVo.setBusinessType(InventoryAuditEnum.INV_MOVE.getBusinessType());
//                    toEisVo.setOwnerId(dInfo.getCargoOwnerId());
//                    toEisVo.setItemId(dInfo.getItemId());
//                    toEisVo.setLotId(dInfo.getBatchId());
//                    String packId = "";
//                    if (CollUtil.isNotEmpty(itemMst.getBaseMtlItemUomConversionsList())) {
//                        packId = itemMst.getBaseMtlItemUomConversionsList().get(0).getItemUomId();
//                    }
//                    if (StringUtils.isNotEmpty(dInfo.getOutContainerCode()) && !"N/A".equals(dInfo.getOutContainerCode())) {
//                        toEisVo.setContainerNo(dInfo.getOutContainerCode());
//                    }
//                    if ("AGVL".equals(outLocator.getWarehouseAreaCategory()) && StringUtils.isNotEmpty(dInfo.getOutGoodsCaseCode()) && !"N/A".equals(dInfo.getOutGoodsCaseCode())) {
//                        toEisVo.setLocNo(dInfo.getOutGoodsCaseCode());
//                        toEisVo.setContainerNo(dInfo.getOutGoodsCaseCode());
//                    }
//                    toEisVo.setPackId(packId);
//                    toEisVo.setWmsLocationCode(outLocator.getWarehouseAreaCode());
//                    toEisVo.setConfirmQty(dInfo.getPieceQuantity().toString());
//                    toEisVos.add(toEisVo);
//                }

//                if (1 == moveRule.getSyncFlag() && StrUtil.isNotBlank(dInfo.getOutStockStatus()) && !dInfo.getOutStockStatus().equals(dInfo.getInStockStatus())) {
//                    // 拼接回传ERP参数
//                    LockToErpVo erpVo = new LockToErpVo();
//                    erpVo.setGoodSn(dInfo.getItemCode());
//                    erpVo.setFLot(dInfo.getManufacturerLotNum());
//                    erpVo.setStockNum(dInfo.getPieceQuantity().toString());
//                    erpVo.setAlterTime(sdf.format(new Date()));
//                    erpVo.setAlterRemark(dInfo.getUnqualifiedReason());
//                    backErpList.add(erpVo);
//                }
            }
        }
//        // 处理库存
//        InvStockCheckMove invStockCheckMove = new InvStockCheckMove();
//        invStockCheckMove.setMoveFlag(0);
//        invStockCheckMove.setLineBillIds(auditParamsVO.getLineBillId());
//        log.info("发送给库存服务的移库数据=>" + JSONObject.toJSONString(invStockCheckMove));
//        // 处理下架库存
//        RestMessage restMessage = stockResource.moveStockCheck(invStockCheckMove);
//        if (!restMessage.isSuccess()) {
//            throw new BizException("远程调用库存服务移库下架错误, 原因=>" + restMessage.getMessage());
//        }
//        // 处理上架库存
//        invStockCheckMove.setMoveFlag(1);
//        log.info("发送给库存服务的移库数据=>" + JSONObject.toJSONString(invStockCheckMove));
//        restMessage = stockResource.moveStockCheck(invStockCheckMove);
//        if (!restMessage.isSuccess()) {
//            throw new BizException("远程调用库存服务移库上架错误, 原因=>" + restMessage.getMessage());
//        }
        // 移库审核完成，生成移库下架任务和移库上架任务，移库上架任务完成后在推送库存状态变更至ERP
//        if (!backErpList.isEmpty()) {
//            // 库存状态变化回传ERP
//            RestMessage<Object> backErpMsg = stockResource.askIntegration(new AskIntegrationVo("stockLock", backErpList));
//            if (null == backErpMsg || !"1".equals(backErpMsg.getCode())) {
//                throw new BizException("库存状态变化回传ERP失败！");
//            }
//        }
        // 移库审核完成，生成移库下架任务和移库上架任务，移库下架任务完成后在推送盘点审核信息至EIS
//        if (!toEisVos.isEmpty()) {
//            // 移库审核完成推送盘点审核信息至EIS
//            eisHandler.inventoryAudit(toEisVos);
//        }
    }

    private void excuteTransferOut2(List<MoveDListVO> moveDListVOList, WmsMoveH moveH) {
        List<WmsMoveD> dList = BeanConvertUtils.convertListTo(moveDListVOList, WmsMoveD::new, (s, t) -> {
            com.prolog.framework.beans.BeanUtils.copyProperties(s, t);
        });
//        excuteTransferOut(dList, moveH);
    }

    /**
     * 移库上/下架确认
     *
     * @param confirmParamsVO 移库确认信息
     */
    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public String confirmMove(MoveConfirmParamsVO confirmParamsVO) {
        WmsMoveDStatus status;
        WmsMoveDStatus preStatus;
        String message = "操作成功！";
        if ("0".equals(confirmParamsVO.getMoveFlag().toString())) {
            preStatus = WmsMoveDStatus.ERP_AUDIT_PASS;
            status = WmsMoveDStatus.WMS_MOVE_OUT;
        } else if ("1".equals(confirmParamsVO.getMoveFlag().toString())) {
            preStatus = WmsMoveDStatus.WMS_MOVE_OUT;
            status = WmsMoveDStatus.WMS_MOVE_IN;
        } else if ("2".equals(confirmParamsVO.getMoveFlag().toString())) {
            preStatus = WMS_MOVE_OUT_AGV;
            status = WmsMoveDStatus.WMS_MOVE_OUT;
        } else if ("3".equals(confirmParamsVO.getMoveFlag().toString())) {
            preStatus = WmsMoveDStatus.WMS_MOVE_IN_AGV;
            status = WmsMoveDStatus.WMS_MOVE_IN;
        } else {
            throw new BizException("未知的库存操作类型");
        }
        UserDTO user = userTemplate.getUserDTO();
        // 用户信息为空随便取一个明细中的用户相关数据组成user对象
        if (ObjectUtils.isNull(user)) {
            log.info("用户为空,取明细中的用户.");
            String[] lineIds = confirmParamsVO.getLineIds();
            WmsMoveD findById = moveDMapper.findById(lineIds[0], WmsMoveD.class);
            AssertUtils.notNull(findById, "查询的第一条明细数据为空!请检查重试!");
            String enterpriseId = findById.getEnterpriseId();
            String userId = findById.getModifier();
            String userName = findById.getModifierName();

            user = new UserDTO();
            user.setEnterpriseId(enterpriseId);
            user.setId(userId);
            user.setUsername(userName);
            user.setEnterpriseName(enterpriseId);
        }
        //		UserDTO user = userTemplate.getUserDTO();
        try {
            log.info("移库请求确认的参数->" + JsonUtils.toString(confirmParamsVO));
            String enterpriseId = user.getEnterpriseId();
            if (StringUtils.isEmpty(enterpriseId)) {
                log.error("企业id为空!");
            }
            if (ObjectUtils.isNull(preStatus)) {
                log.error("移库原状态为空!");
            }
            log.info("移库原状态->" + preStatus.getStatus());
            if (ObjectUtils.isNull(status)) {
                log.error("移库新状态为空!");
            }
            log.info("移库新状态->" + status.getStatus());
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        Set<String> statusSet = new HashSet<>();
        statusSet.add(preStatus.getStatus());
        if ("0".equals(confirmParamsVO.getMoveFlag().toString())){
            statusSet.add(WMS_MOVE_OUT_AGV.getStatus());
        }
        Criteria criteria = Criteria.forClass(WmsMoveD.class);
        criteria.restrictionBuilder()
                .eq("enterpriseId", user.getEnterpriseId())
                .in("status", statusSet.toArray())
                .in("moveLineId", confirmParamsVO.getLineIds());
        criteria.buildRestriction();

        //支持修改上架货位
        if (CollUtil.isNotEmpty(confirmParamsVO.getMoveDetailList()) && ("1".equals(confirmParamsVO.getMoveFlag().toString()) || "3".equals(confirmParamsVO.getMoveFlag().toString()))){
            Set<String> locatorIdSet = confirmParamsVO.getMoveDetailList().stream().map(WmsMoveD::getInLocatorId).collect(Collectors.toSet());
            String[] locatorIdList = locatorIdSet.toArray(new String[]{});
            RestMessage<List<WarehouseLocatorBasicInfo>> locationBasicInfos = warehouseClient.getLocationBasicInfos(locatorIdList);
            if (!locationBasicInfos.isSuccess() || locationBasicInfos.getData() == null) {
                throw new BizException("查询货位关联信息失败!请重试或联系管理员!");
            }

            //查询对应的移库单
            List<WmsMoveD> moveDs = moveDMapper.findByCriteria(criteria);
            if (CollUtil.isNotEmpty(moveDs)){
                List<WmsMoveD> modifyLocatorIdList = new ArrayList<>();
                //循环比对移入货位是否修改
                for (WmsMoveD moveD : confirmParamsVO.getMoveDetailList()) {
                    for (WmsMoveD d : moveDs) {
                        if (moveD.getMoveLineId().equals(d.getMoveLineId()) && !moveD.getInLocatorId().equals(d.getInLocatorId())){
                            //修改了上架货位，需要更新货位以及对应的预占信息
                            //校验货位信息
                            WarehouseLocatorBasicInfo locatorBasicInfo = locationBasicInfos.getData().stream().filter(var-> moveD.getInLocatorId().equals(var.getWarehouseLocatorId())).findFirst().orElse(null);
                            if (locatorBasicInfo == null){
                                throw new BizException("货位【" + moveD.getInLocatorId() + "】不存在！请检查");
                            }
                            //当前货物是否满足货位分配策略
                            //当前移入货位不符合上架策略及货位限定约束的可上架货位
                            RestMessage<BaseMtlItemMst> itemMsg = itemResource.item(d.getItemId());
                            if (!itemMsg.isSuccess() || itemMsg.getData() == null){
                                throw new BizException("商品不存在");
                            }
                            RestMessage<Page<WarehouseLocatorBasicInfo>> limitMsg = controlClient.getInboundLocatorLimit(d.getItemId(), itemMsg.getData().getCargoOwnerId(), locatorBasicInfo.getWarehouseId(), locatorBasicInfo.getWarehouseAreaCategory(), 1, 999);
                            Assert.isTrue(limitMsg.isSuccess() && null != limitMsg.getData() && CollUtil.isNotEmpty(limitMsg.getData().getList()), "当前移入货位【" + locatorBasicInfo.getWarehouseLocatorCode() + "】不符合上架策略及货位限定约束的可上架货位");
                            WarehouseLocatorBasicInfo warehouseLocatorBasicInfo = limitMsg.getData().getList().stream().filter(var -> locatorBasicInfo.getWarehouseLocatorId().equals(var.getWarehouseLocatorId())).findFirst().orElse(null);
                            Assert.isTrue(null  != warehouseLocatorBasicInfo, "当前移入货位【" + locatorBasicInfo.getWarehouseLocatorCode() + "】不符合上架策略及货位限定约束的可上架货位");
                            //更新移入货位
                            moveDMapper.updateMapById(d.getLineId(), MapUtils.put("inLocatorId", moveD.getInLocatorId()).getMap(), WmsMoveD.class);
                            //更新预占信息
                            modifyLocatorIdList.add(d);
                        }
                    }
                }

                if (CollUtil.isNotEmpty(modifyLocatorIdList)){
                    //1、去修改库存预占表的货位
                    List<Map<String, Object>> params = new ArrayList<>();
                    for (WmsMoveD wmsMoveD : modifyLocatorIdList) {
                        Map<String, Object> map = new HashMap<>();
                        map.put("whLocatorId", wmsMoveD.getInLocatorId());
                        map.put("lineBillId", wmsMoveD.getMoveLineId());
                        map.put("billType", "IN");
                        params.add(map);
                    }
                    stockResource.updateLocator(params);
                }
            }
        }
        // 更新状态
        WmsMoveD dEntity = new WmsMoveD();
        dEntity.setStatus(status.getStatus());
        ModelUtils.setBaseInfo(dEntity, ModelUtils.OpType.UPDATE, user);
        moveDMapper.updateFieldsByCriteria(dEntity, FieldSelector.newInstance().include("status"), criteria);
        Set<String> syncIds = null;
        List<MoveSyncInfoVO> syncInfoList  = new ArrayList<>();
        List<MoveSyncInfoVO> syncInfoList2 = new ArrayList<>();
        List<MoveSyncInfoVO> syncInfoList3 = new ArrayList<>();
        // 查询所有需要同步的主表信息
        String[] statusList = new String[]{WmsMoveDStatus.NEW.getStatus(), WmsMoveDStatus.WMS_AUDIT_PASS.getStatus(), WmsMoveDStatus.WMS_SYNC.getStatus(), WmsMoveDStatus.ERP_AUDIT_PASS.getStatus(), WmsMoveDStatus.WMS_MOVE_OUT.getStatus()};
        // 上架完成需要同步至ERP
        if ("1".equals(confirmParamsVO.getMoveFlag().toString()) || "3".equals(confirmParamsVO.getMoveFlag().toString())) {
            // 查询WMS发起的操作完毕的移库单
            syncInfoList = moveHMapper.getMoveDoneByLineBill(user.getEnterpriseId(), WmsMoveBillType.WMS.name(), WmsMoveSubBillType.MOVE_AREA.getType(), confirmParamsVO.getLineIds(), statusList);
            syncInfoList2 = moveHMapper.getMoveDoneByLineBill(user.getEnterpriseId(), WmsMoveBillType.ERP.name(), WmsMoveSubBillType.MOVE_AREA.getType(), confirmParamsVO.getLineIds(), statusList);
            syncInfoList3 = moveHMapper.getMoveDoneByLineBill(user.getEnterpriseId(), WmsMoveBillType.GSP.name(), WmsMoveSubBillType.MOVE_AREA.getType(), confirmParamsVO.getLineIds(), statusList);

            // 查询货主转换单发起的操作完毕的移库单
            List<MoveSyncInfoVO> cargoChangeList = moveHMapper.getMoveDoneByLineBill(user.getEnterpriseId(), WmsMoveBillType.SC.name(), WmsMoveSubBillType.MOVE_AREA.getType(), confirmParamsVO.getLineIds(), statusList);
            Set<String> cargoChangeHeaderIds = cargoChangeList.stream().map(MoveSyncInfoVO::getBillId).collect(Collectors.toSet());
            // 判断是否存在货主转换单移库
            if (CollectionUtil.isNotEmpty(cargoChangeHeaderIds)) {
                cargoChangeService.changeCargoComplete(cargoChangeHeaderIds);
            }
        }

        // 查询货主转换单发起的操作完毕的移库单
        List<MoveSyncInfoVO> cargoChangeList = moveHMapper.getMoveDoneByLineBill(user.getEnterpriseId(), WmsMoveBillType.SC.name(), WmsMoveSubBillType.MOVE_AREA.getType(), confirmParamsVO.getLineIds(), null);
        Collection<String> detailIds = cargoChangeList.stream().map(MoveSyncInfoVO::getMoveLineId).collect(Collectors.toSet());
        Collection<String> lineIds = CollectionUtil.newArrayList(confirmParamsVO.getLineIds());
        if (CollectionUtil.isNotEmpty(detailIds)) {
            lineIds = CollectionUtil.subtract(lineIds, detailIds);
        }
        if (CollectionUtil.isNotEmpty(lineIds)) {
            // 移库确认
            InvStockCheckMove invStockCheckMove = new InvStockCheckMove();
            Integer moveFlag = confirmParamsVO.getMoveFlag();
            if (moveFlag != null && 2 == moveFlag) {
                moveFlag = 0;
            }
            if (moveFlag != null && 3 == moveFlag) {
                moveFlag = 1;
            }
            invStockCheckMove.setMoveFlag(moveFlag);
            invStockCheckMove.setLineBillIds(lineIds.toArray(new String[0]));
            try {
                log.info("发送给库存服务的移库数据=>" + JsonUtils.toString(invStockCheckMove));
            } catch (JsonProcessingException e) {
            }
            RestMessage restMessage = stockResource.moveStockCheck(invStockCheckMove);
            if (!restMessage.isSuccess()) {
                throw new BizException("远程调用库存服务移库错误,原因=>" + restMessage.getMessage());
            }
        }

        //senSyncMessage(syncIds);
        //库存状态变化回传OMS
        if (CollectionUtil.isNotEmpty(syncInfoList)) {
            syncIds = genOmsStockAdjustResult(syncInfoList, user);
        } else {
            if (CollectionUtil.isNotEmpty(syncInfoList2)) {
                syncIds = genOmsStockAdjustResult(syncInfoList2, user);
            }
        }
        if (CollectionUtil.isNotEmpty(syncInfoList3)) {
            syncIds = genOmsStockAdjustResult(syncInfoList3, user);
        }


        //质量复查下架确认回传
        gspQRview(confirmParamsVO, user);

        // 执行设备任务--不是鹰江的逻辑
        // excuteTransfer(confirmParamsVO);

        if (Objects.equals(WmsMoveDStatus.WMS_MOVE_OUT.getStatus(), status.getStatus())) { //如果是下架
            //回告Eis托盘回库
            ThreadUtil.execAsync(()-> {this.moveToEis(confirmParamsVO.getLineIds());});
        }
        return message;
    }

    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public void moveDownConfirm(MoveDListVO wmsMoveD) {
        if (ObjectUtils.isNull(wmsMoveD)) {
            throw new BizException("请求入参不能为空!");
        }
        if (StringUtils.isBlank(wmsMoveD.getMoveLineId())) {
            throw new BizException("移库任务明细ID不能为空!");
        }

        UserDTO user = userTemplate.getUserDTO();
        if (ObjectUtils.isNull(user)) {
            throw new BizException("用户信息获取失败!");
        }

        // 查询明细
        String[] moveLines = { wmsMoveD.getMoveLineId() };
        MoveQueryDVO queryVO = new MoveQueryDVO();
        queryVO.setMoveLineIds(moveLines);
        List<MoveDListVO> dList = moveDMapper.selectMoveDInfoList(user.getEnterpriseId(), queryVO);
        if (CollectionUtil.isEmpty(dList)) {
            throw new BizException("未找到待移库下架的任务信息！");
        }
        MoveDListVO moveTask = dList.get(0);
        if (!WmsMoveDStatus.WMS_AUDIT_PASS.getStatus().equals(moveTask.getStatus())
                && !WmsMoveDStatus.ERP_AUDIT_PASS.getStatus().equals(moveTask.getStatus())) {
            throw new BizException("移库任务未审核通过!");
        }
        // 更新状态为WMS已下架
        this.updateMoveTaskDByCriteria(user, WmsMoveDStatus.WMS_MOVE_OUT.getStatus(), moveLines);

        // 处理库存
        InvStockCheckMove invStockCheckMove = new InvStockCheckMove();
        invStockCheckMove.setMoveFlag(0);
        invStockCheckMove.setLineBillIds(moveLines);
        log.info("发送给库存服务的移库下架数据=>" + JSONObject.toJSONString(invStockCheckMove));
        // 处理下架库存
        RestMessage restMessage = stockResource.moveStockCheck(invStockCheckMove);
        if (!restMessage.isSuccess()) {
            throw new BizException("远程调用库存服务移库下架错误, 原因=>" + restMessage.getMessage());
        }

        RestMessage<List<WarehouseLocatorBasicInfo>> locatorResult = warehouseClient.getLocationBasicInfos(new String[]{moveTask.getOutLocatorId()});
        if (!locatorResult.isSuccess() || CollectionUtil.isEmpty(locatorResult.getData())) {
            throw new BizException("获取库位信息失败");
        }
        WarehouseLocatorBasicInfo outLocator = locatorResult.getData().get(0);
        // 若选择审核的移库记录中【移出库别】=【设备库】，则推送盘点审核信息至EIS
        if (PICK_MODE_DEVICE.equals(outLocator.getPickMode())) {
            // 查询商品资料
            RestMessage<BaseMtlItemMst> rest = itemResource.item(moveTask.getItemId());
            if (!rest.isSuccess() || null == rest.getData()) {
                throw new RuntimeException("查询商品服务失败！" + rest.getMessage());
            }
            BaseMtlItemMst itemMst = rest.getData();

            // 构建并推送盘点审核信息至EIS
            InventoryAuditVO toEisVo = new InventoryAuditVO();
            toEisVo.setMsgId(cn.hutool.core.util.IdUtil.fastSimpleUUID());
            toEisVo.setTaskLineId(moveTask.getMoveLineId());
            toEisVo.setWarehouseId(outLocator.getWarehouseId());
            toEisVo.setWorkType(InventoryAuditEnum.INV_MOVE.getWorkType());
            toEisVo.setBusinessType(InventoryAuditEnum.INV_MOVE.getBusinessType());
            toEisVo.setOwnerId(moveTask.getCargoOwnerId());
            toEisVo.setItemId(moveTask.getItemId());
            toEisVo.setLotId(moveTask.getBatchId());
            String packId = "";
            if (CollUtil.isNotEmpty(itemMst.getBaseMtlItemUomConversionsList())) {
                packId = itemMst.getBaseMtlItemUomConversionsList().get(0).getItemUomId();
            }
            if (StringUtils.isNotEmpty(moveTask.getOutContainerCode()) && !"N/A".equals(moveTask.getOutContainerCode())) {
                toEisVo.setContainerNo(moveTask.getOutContainerCode());
            }
            if ("AGVL".equals(outLocator.getWarehouseAreaCategory()) && StringUtils.isNotEmpty(moveTask.getOutGoodsCaseCode())
                    && !"N/A".equals(moveTask.getOutGoodsCaseCode())) {
                toEisVo.setLocNo(moveTask.getOutGoodsCaseCode());
                toEisVo.setContainerNo(moveTask.getOutGoodsCaseCode());
            }
            toEisVo.setPackId(packId);
            toEisVo.setWmsLocationCode("MJK".equals(outLocator.getWarehouseAreaCode()) ? "MJZ" : outLocator.getWarehouseAreaCode());
            toEisVo.setConfirmQty(moveTask.getPieceQuantity().toString());

            // 移库审核完成推送盘点审核信息至EIS
            eisHandler.inventoryAudit(Collections.singletonList(toEisVo));

            // 如果是立库，并且不是拆零区
            if ("AGVL".equals(outLocator.getWarehouseAreaCategory()) || StringUtils.isBlank(moveTask.getOutContainerCode())) {
                return;
            }
            // 根据托盘查询库存数量
            WmsInvStockLot stockLot = new WmsInvStockLot();
            stockLot.setContainerCode(moveTask.getOutContainerCode());
            stockLot.setWarehouseId(moveTask.getWarehouseId());
            stockLot.setItemId(moveTask.getItemId());
            List<WmsInvStockLot> stockLotList = RestMsgHelper.unpack(stockResource.queryStockList(stockLot));
            if (CollectionUtil.isEmpty(stockLotList)) {
                return;
            }
            BigDecimal stockQty = stockLotList.stream().map(WmsInvStockLot::getQuantity).reduce(BigDecimal::add).get();
            BigDecimal oddQty = stockQty.divideAndRemainder(itemMst.getCaseConversionRate())[1];
            // 判断是否存在零散数，如果存在零散数，则调用EIS库存锁定接口锁定EIS库存
            if (oddQty.compareTo(BigDecimal.ZERO) <= 0) {
                return;
            }

            InventoryLockVO inventoryLockVO = new InventoryLockVO();
            inventoryLockVO.setWarehouseId(moveTask.getWarehouseId());
            inventoryLockVO.setItemId(moveTask.getItemId());
            // 锁定当前托盘剩余库存
            inventoryLockVO.setConfirmQty(stockQty.doubleValue());
            inventoryLockVO.setContainerNo(moveTask.getOutContainerCode());
            inventoryLockVO.setWmsLocationCode("MJK".equals(outLocator.getWarehouseAreaCode()) ? "MJZ" : outLocator.getWarehouseAreaCode());
            inventoryLockVO.setOwnerId(moveTask.getCargoOwnerId());
            inventoryLockVO.setLotId(moveTask.getBatchId());
            inventoryLockVO.setWorkType(InventoryLockEnum.INV_LOCK.getWorkType());
            inventoryLockVO.setBusinessType(InventoryLockEnum.INV_LOCK.getBusinessType());
            String id = String.valueOf(SnowFlakeUtils.getId());
            inventoryLockVO.setMsgId(id);
            inventoryLockVO.setTaskId(id);
            inventoryLockVO.setTaskLineId(moveTask.getMoveLineId());
            if (CollUtil.isNotEmpty(itemMst.getBaseMtlItemUomConversionsList())) {
                packId = itemMst.getBaseMtlItemUomConversionsList().get(0).getItemUomId();
            }
            inventoryLockVO.setPackId(packId);
            log.info("移库下架确认库存锁定回传eis入参:{}", JSONObject.toJSONString(inventoryLockVO));
            eisHandler.inventoryLock(Collections.singletonList(inventoryLockVO));
        }
    }

    private void updateMoveTaskDByCriteria(UserDTO user, String status, String[] moveLineIds) {
        Criteria criteria = Criteria.forClass(WmsMoveD.class);
        criteria.restrictionBuilder()
                .eq("enterpriseId", user.getEnterpriseId())
                .in("moveLineId", moveLineIds);
        criteria.buildRestriction();

        // 更新状态为WMS已下架
        WmsMoveD dEntity = new WmsMoveD();
        dEntity.setStatus(status);
        ModelUtils.setBaseInfo(dEntity, ModelUtils.OpType.UPDATE, user);
        FieldSelector fields = FieldSelector.newInstance().include("status", "modifier", "gmtModified", "modifierName");
        moveDMapper.updateFieldsByCriteria(dEntity, fields, criteria);
    }

    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public void moveUpConfirm(MoveDListVO wmsMoveD) {
        if (ObjectUtils.isNull(wmsMoveD)) {
            throw new BizException("请求入参不能为空!");
        }
        if (StringUtils.isBlank(wmsMoveD.getMoveLineId())) {
            throw new BizException("移库任务明细ID不能为空!");
        }
        if (StringUtils.isBlank(wmsMoveD.getInLocatorCode())) {
            throw new BizException("移库货位编码不能为空!");
        }
        UserDTO user = userTemplate.getUserDTO();
        if (ObjectUtils.isNull(user)) {
            throw new BizException("用户信息获取失败!");
        }

        // 查询明细
        String[] moveLines = { wmsMoveD.getMoveLineId() };
        MoveQueryDVO queryVO = new MoveQueryDVO();
        queryVO.setMoveLineIds(moveLines);
        List<MoveDListVO> dList = moveDMapper.selectMoveDInfoList(user.getEnterpriseId(), queryVO);
        if (CollectionUtil.isEmpty(dList)) {
            throw new BizException("未找到待移库上架的任务信息！");
        }
        MoveDListVO moveTask = dList.get(0);
        if (!WmsMoveDStatus.WMS_MOVE_OUT.getStatus().equals(moveTask.getStatus())) {
            throw new BizException("移库下架任务未完成!");
        }
        // 获取库位信息
        String[] locatorIds = new String[] {moveTask.getInLocatorId(), moveTask.getOutLocatorId()};
        RestMessage<List<WarehouseLocatorBasicInfo>> locatorResult = warehouseClient.getLocationBasicInfos(locatorIds);
        if (!locatorResult.isSuccess() || CollectionUtil.isEmpty(locatorResult.getData())) {
            throw new BizException("获取货位信息失败!");
        }
        WarehouseLocatorBasicInfo inLocator = locatorResult.getData().stream().filter(x -> x.getWarehouseLocatorId().equals(moveTask.getInLocatorId())).findFirst().orElse(null);
        if (inLocator == null) {
            throw new BizException("移入库位不存在或已失效!");
        }
        if (!inLocator.getWarehouseLocatorCode().equals(wmsMoveD.getInLocatorCode())) {
            throw new BizException("实际上架货位与推荐货位不一致!");
        }
        // 如果移入货位是拆零区，则货格号不能为空
        if ("AGVL".equals(inLocator.getWarehouseAreaCategory())
                && (StringUtils.isBlank(wmsMoveD.getInGoodsCaseCode()) || "N/A".equals(wmsMoveD.getInGoodsCaseCode()) )) {
            throw new BizException("移库货位为拆零区，移入货格号不能为空!");
        }
//        WarehouseLocatorBasicInfo outLocator = locatorResult.getData().stream().filter(x -> x.getWarehouseLocatorId().equals(moveTask.getOutLocatorId())).findFirst().orElse(null);
//        if (outLocator == null) {
//            throw new BizException("移出库位不存在或已失效!");
//        }

        // 更新状态为WMS已下架
        this.updateMoveTaskDByCriteria(user, WmsMoveDStatus.WMS_MOVE_IN.getStatus(), moveLines);

        // 处理库存
        InvStockCheckMove invStockCheckMove = new InvStockCheckMove();
        invStockCheckMove.setMoveFlag(1);
        invStockCheckMove.setLineBillIds(moveLines);
        log.info("发送给库存服务的移库上架数据=>" + JSONObject.toJSONString(invStockCheckMove));
        // 处理下架库存
        RestMessage restMessage = stockResource.moveStockCheck(invStockCheckMove);
        if (!restMessage.isSuccess()) {
            throw new BizException("远程调用库存服务移库下架错误, 原因=>" + restMessage.getMessage());
        }
        // 移库上架完成推送盘点审核信息至EIS
        this.moveUpFinishedPushAuditToEis(moveTask, inLocator, wmsMoveD.getInGoodsCaseCode());

        // 根据仓库查询移库规则
//        RestMessage<List<MoveRuleDTO>> moveRuleResult = strategyClient.getMoveList(moveTask.getWarehouseId(), null, null);
//        if (!moveRuleResult.isSuccess() || CollectionUtil.isEmpty(moveRuleResult.getData())) {
//            throw new BizException("获取移库规则失败");
//        }
//        MoveRuleDTO moveRule = moveRuleResult.getData().stream().filter(x -> x.getSrcWarehouseCategory().equals(outLocator.getWarehouseAreaCategory()) && x.getDestWarehouseCategory().equals(inLocator.getWarehouseAreaCategory())).findFirst().orElse(null);
//        if (moveRule == null) {
//            throw new BizException("未找到有效的移库规则");
//        }
//        // 回传ERP
//        if (1 == moveRule.getSyncFlag() && StrUtil.isNotBlank(moveTask.getOutStockStatus()) && !moveTask.getOutStockStatus().equals(moveTask.getInStockStatus())) {
//            // 拼接回传ERP参数
//            LockToErpVo erpVo = new LockToErpVo();
//            erpVo.setGoodSn(moveTask.getItemCode());
//            erpVo.setFLot(moveTask.getManufacturerLotNum());
//            erpVo.setStockNum(moveTask.getPieceQuantity().toString());
//            // 精确到毫秒
//            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
//            erpVo.setAlterTime(sdf.format(new Date()));
//            erpVo.setAlterRemark(moveTask.getUnqualifiedReason());
//            erpVo.setBeforeWNo(outLocator.getWarehouseAreaCategory());
//            erpVo.setAfterWNo(inLocator.getWarehouseAreaCategory());
//
//            // 库存状态变化回传ERP
//            RestMessage<Object> backErpMsg = stockResource.askIntegration(new AskIntegrationVo("stockLock", Collections.singletonList(erpVo)));
//            if (null == backErpMsg || !"1".equals(backErpMsg.getCode())) {
//                throw new BizException("库存状态变化回传ERP失败！");
//            }
//        }
    }

    @Override
    public void checkGoodsCaseCodeStock(MoveDListVO wmsMoveD) {
        if (null == wmsMoveD) {
            throw new RuntimeException("请求入参不能为空！");
        }
        if (StringUtils.isBlank(wmsMoveD.getInGoodsCaseCode())) {
            throw new RuntimeException("货格号不能为空！");
        }
        if (StringUtils.isBlank(wmsMoveD.getItemId())) {
            throw new RuntimeException("待上架物料ID不能为空！");
        }
        // 查询商品资料
        BaseMtlItemMst moveItemInfo = this.getItemInfoById(wmsMoveD.getItemId());
        // 根据货格号调用EIS接口查询货格号库存
        // 实时调用EIS接口，拿到返回数据
        QueryContainerPositionVO positionVO = new QueryContainerPositionVO();
        positionVO.setPageNum(1);
        positionVO.setPageSize(10000);
        positionVO.setContainerNo(wmsMoveD.getInGoodsCaseCode());
        positionVO.setEisStorageCode("AGVL");
        List<QueryContainerPositionDTO> eisStockList = eisHandler.queryContainerPosition(positionVO);
        if (CollectionUtil.isEmpty(eisStockList)) {
            return;
        }
        // 查询商品资料
        BaseMtlItemMst eisItemInfo = this.getItemInfoById(eisStockList.get(0).getItemId());
        String eisCategory = eisItemInfo.getBaseMtlItemHealth().getItemHealthCategory();
        String moveCategory = moveItemInfo.getBaseMtlItemHealth().getItemHealthCategory();
        if (StringUtils.isBlank(eisCategory) || !eisCategory.equals(moveCategory)) {
            throw new RuntimeException("货格中的商品大类【" + eisCategory + "】和待上架的商品大类【" + moveCategory + "】不一致！");
        }
    }

    private BaseMtlItemMst getItemInfoById(String itemId) {
        // 查询商品资料
        RestMessage<BaseMtlItemMst> rest = itemResource.item(itemId);
        if (!rest.isSuccess() || null == rest.getData()) {
            throw new RuntimeException("查询商品服务失败！" + rest.getMessage());
        }
        return rest.getData();
    }

    private void moveUpFinishedPushAuditToEis(MoveDListVO moveTask, WarehouseLocatorBasicInfo locator, String inGoodsCaseCode) {
        // 移入货位是拆零区，则根据货格号查询EIS库存，推送盘点审核信息至EIS
        if (!"AGVL".equals(locator.getWarehouseAreaCategory())) {
            return;
        }
        // 查询商品资料
        BaseMtlItemMst moveItemInfo = this.getItemInfoById(moveTask.getItemId());

        // 根据货格号调用EIS接口查询货格号库存
        // 实时调用EIS接口，拿到返回数据
        QueryContainerPositionVO positionVO = new QueryContainerPositionVO();
        positionVO.setPageNum(1);
        positionVO.setPageSize(100);
        positionVO.setContainerNo(inGoodsCaseCode);
        positionVO.setEisStorageCode(locator.getWarehouseAreaCategory());
        List<QueryContainerPositionDTO> eisStockList = eisHandler.queryContainerPosition(positionVO);
        if (CollectionUtil.isNotEmpty(eisStockList)) {
            // 查询商品资料
            BaseMtlItemMst eisItemInfo = this.getItemInfoById(eisStockList.get(0).getItemId());
            String eisCategory = eisItemInfo.getBaseMtlItemHealth().getItemHealthCategory();
            String moveCategory = moveItemInfo.getBaseMtlItemHealth().getItemHealthCategory();
            if (StringUtils.isBlank(eisCategory) || !eisCategory.equals(moveCategory)) {
                throw new RuntimeException("货格中的商品大类【" + eisCategory + "】和待上架的商品大类【" + moveCategory + "】不一致！");
            }
            Double sumEisQty = eisStockList.stream().filter(eisStock -> eisStock.getItemId().equals(moveTask.getItemId())
                            && eisStock.getLotId().equals(moveTask.getBatchId())).mapToDouble(QueryContainerPositionDTO::getQty).sum();
            // 设置最后上架数量为EIS库存数量 + 移库数量
            moveTask.setPieceQuantity(NumberUtil.add(sumEisQty, moveTask.getPieceQuantity()));
        }

        // 构建并推送盘点审核信息至EIS
        InventoryAuditVO toEisVo = new InventoryAuditVO();
        toEisVo.setMsgId(cn.hutool.core.util.IdUtil.fastSimpleUUID());
        toEisVo.setTaskLineId(moveTask.getMoveLineId());
        toEisVo.setWarehouseId(locator.getWarehouseId());
        toEisVo.setWorkType(InventoryAuditEnum.INV_ADJUST.getWorkType());
        toEisVo.setBusinessType(InventoryAuditEnum.INV_ADJUST.getBusinessType());
        toEisVo.setOwnerId(moveTask.getCargoOwnerId());
        toEisVo.setItemId(moveTask.getItemId());
        toEisVo.setLotId(moveTask.getBatchId());
        String packId = "";
        if (CollUtil.isNotEmpty(moveItemInfo.getBaseMtlItemUomConversionsList())) {
            packId = moveItemInfo.getBaseMtlItemUomConversionsList().get(0).getItemUomId();
        }
        toEisVo.setLocNo(inGoodsCaseCode);
        toEisVo.setContainerNo(inGoodsCaseCode);

        toEisVo.setPackId(packId);
        toEisVo.setWmsLocationCode("MJK".equals(locator.getWarehouseAreaCode()) ? "MJZ" : locator.getWarehouseAreaCode());
        toEisVo.setInventoryQty(moveTask.getPieceQuantity().toString());
        toEisVo.setConfirmQty(moveTask.getPieceQuantity().toString());

        // 移库审核完成推送盘点审核信息至EIS
        eisHandler.inventoryAudit(Collections.singletonList(toEisVo));
    }

    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public String updateToW23(MoveConfirmParamsVO moveConfirmParamsVO) {
        if (ArrayUtil.isNotEmpty(moveConfirmParamsVO.getLineIds())) {
            for (String lineId : moveConfirmParamsVO.getLineIds()) {
                moveDMapper.updateMapById(lineId, MapUtils.put("status", WMS_MOVE_OUT_AGV.getStatus()).getMap(), WmsMoveD.class);
            }
//            return this.confirmMove(moveConfirmParamsVO);
            return "success";
        }
        throw new BizException("id不能为空");
    }

    private void moveToEis(
            String[] lineIds
    ) {
        Criteria wmsMovedCriteria = Criteria.forClass(WmsMoveD.class);
        Restriction r1 = Restrictions.in("moveLineId", lineIds);
        wmsMovedCriteria.setRestriction(r1);
        //查询出确认的移库明细
        List<WmsMoveD> wmsMoveDList = moveDMapper.findByCriteria(wmsMovedCriteria);
        //获取库位信息
        // 获取库位信息
        String[] locatorIds = wmsMoveDList.stream().map(WmsMoveD::getOutLocatorId).distinct().toArray(String[]::new);

        RestMessage<List<WarehouseLocatorBasicInfo>> locatorResult = warehouseClient.getLocationBasicInfos(locatorIds);
        if (!locatorResult.isSuccess() || CollectionUtil.isEmpty(locatorResult.getData())) {
            throw new BizException("获取库位信息失败");
        }

        //待回告EIS的数据
        List<WmsMoveD> agvMoveList = new ArrayList<>();
        for (WmsMoveD wmsMoveD : wmsMoveDList) {
            for (WarehouseLocatorBasicInfo datum : locatorResult.getData()) {
                if (wmsMoveD.getOutLocatorId().equals(datum.getWarehouseLocatorId())) {
                    if (datum.getPickMode().equals(PICK_MODE_DEVICE) && !datum.getWarehouseAreaCategory().equals("AGVL") && StrUtil.isNotEmpty(wmsMoveD.getOutContainerCode())) {
                        agvMoveList.add(wmsMoveD);
                    }
                    break;
                }
            }
        }

        if (CollUtil.isNotEmpty(agvMoveList)){
            Set<String> itemIds = agvMoveList.stream().filter(var->StrUtil.isNotEmpty(var.getItemId())).map(WmsMoveD::getItemId).collect(Collectors.toSet());
            RestMessage<List<BaseMtlItemMst>> itemMsg = itemResource.getListByIds(itemIds);
            if (!itemMsg.isSuccess() && CollUtil.isEmpty(itemMsg.getData())){
                throw new BizException("获取商品信息失败！");
            }
            Map<String, List<WmsMoveD>> collect = agvMoveList.stream().collect(Collectors.groupingBy(WmsMoveD::getOutContainerCode));
            //判断托盘是否还有库存
            ByContainerVo byContainerVo = new ByContainerVo();
            byContainerVo.setContainerCodes(new ArrayList<>(collect.keySet()));
            RestMessage<Map<String, List<WmsInvStockQtyVO>>> containerStockMsg = stockResource.listStockLotByContainerCode(byContainerVo);
            for (String containerCode : collect.keySet()) {
                int unconfirmnum = moveDMapper.getUncomfirmContainerNum(WmsMoveDStatus.ERP_AUDIT_PASS.getStatus(), lineIds, containerCode);
                if (unconfirmnum > 0) {
                } else {
                    List<WmsMoveD> dList = collect.get(containerCode);
                    ContainerConfirmDto vo = new ContainerConfirmDto();
                    vo.setContainerNo(containerCode);
                    //查询托盘库存，如果为0则为空托
                    String containerType = "3";
                    boolean isAllZero = true;
                    String warehouseCategory = "";
                    if (containerStockMsg.isSuccess() && CollUtil.isNotEmpty(containerStockMsg.getData())) {
                        List<WmsInvStockQtyVO> lotList = containerStockMsg.getData().get(containerCode);
                        for (WmsInvStockQtyVO stockQtyVO : lotList) {
                            if (isAllZero && stockQtyVO.getQuantity().doubleValue() > 0) {
                                isAllZero = false;
                            }
                            if (StrUtil.isEmpty(warehouseCategory)) {
                                warehouseCategory = stockQtyVO.getWarehouseAreaCategory();
                            }
                        }
                    }
                    if (isAllZero) {
                        if (warehouseCategory.equals("MJK")) {
                            containerType = "2";
                        }
                        if (warehouseCategory.equals("AGVZ")) {
                            containerType = "1";
                        }
                    } else {
                        containerType = "3";
                    }
                    vo.setContainerType(containerType);
                    List<EisConfirmSubVo> confirmVOS = new ArrayList<>();
                    for (WmsMoveD wmsMoveD : dList) {
                        BaseMtlItemMst itemMst = itemMsg.getData().stream().filter(var -> wmsMoveD.getItemId().equals(var.getItemId())).findFirst().orElse(null);
                        WarehouseLocatorBasicInfo locator = locatorResult.getData().stream().filter(var -> var.getWarehouseLocatorId().equals(wmsMoveD.getOutLocatorId())).findFirst().orElse(null);
                        if (null == itemMst) {
                            throw new BizException("商品信息不存在！");
                        }
                        if (null == locator) {
                            throw new BizException("查询货位信息失败！");
                        }
                        EisConfirmSubVo subVo = new EisConfirmSubVo();
                        subVo.setMsgId(cn.hutool.core.util.IdUtil.fastSimpleUUID());
                        subVo.setTaskId(wmsMoveD.getBillId());
                        subVo.setSeqNo(wmsMoveD.getLineId());
                        subVo.setBusinessType("20");
                        subVo.setWorkType("20");
                        subVo.setItemId(wmsMoveD.getItemId());
                        subVo.setLotId(wmsMoveD.getBatchId());
                        subVo.setOwnerId(itemMst.getCargoOwnerId());
                        String packId = "";
                        if (CollUtil.isNotEmpty(itemMst.getBaseMtlItemUomConversionsList())) {
                            packId = itemMst.getBaseMtlItemUomConversionsList().get(0).getItemUomId();
                        }
                        subVo.setPackId(packId);
                        subVo.setWarehouseId("JXYJ");
                        subVo.setWmslocationCode(locator.getWarehouseLocatorCode());
                        confirmVOS.add(subVo);
                    }
                    vo.setSubList(confirmVOS);
//                    stockResource.askIntegration(new AskIntegrationVo("confirmContainer", vo));
                    //FIXME:调接口封装参数
//                    mqProducer.operationComplete(vo);
                    //释放容器
                    if (!containerType.equals("3")) {
                        ContainerQueryVO containerQueryVO = new ContainerQueryVO();
                        containerQueryVO.setContainerCode(vo.getContainerNo());
                        containerQueryVO.setEnterpriseId(dList.get(0).getEnterpriseId());
                        containerQueryVO.setWarehouseId(locatorResult.getData().get(0).getWarehouseId());
                        RestMessage<Object> release = warehouseClient.release(containerQueryVO);
                        log.info("托盘库存已经为0，托盘解锁->{},{}", JSONObject.toJSONString(containerQueryVO), JSONObject.toJSONString(release));
                    }
                }
            }
            //如果移入的货位是AGVZ/MJK 调用EIS回告入库任务
        }
    }


    private void gspQRview(MoveConfirmParamsVO confirmParamsVO, UserDTO user) {
        MoveQueryDVO moveQueryDVO = new MoveQueryDVO();
        moveQueryDVO.setMoveLineIds(confirmParamsVO.getLineIds());
        List<MoveDListVO> moveDListVOS = moveDMapper.selectMoveDInfoList(user.getEnterpriseId(), moveQueryDVO);
        if (CollectionUtil.isNotEmpty(moveDListVOS)) {
            for (MoveDListVO moveDListVO : moveDListVOS) {
                if ("GSP_QUALITY".equals(moveDListVO.getMoveBillType()) && 1 == confirmParamsVO.getMoveFlag()) {
                    String lineId = moveDListVO.getLineId();
                    RestMessage restMessage = gspClient.confirmQualityReviewMove(new String[]{lineId});
                    if (!restMessage.isSuccess()) {
                        throw new BizException(restMessage.getMessage());
                    }
                }
            }
        }
    }

    private void excuteTransfer(MoveConfirmParamsVO confirmParamsVO) {
        if ("0".equals(confirmParamsVO.getMoveFlag().toString())) {
            log.info("执行移库发送agv上架逻辑--------------------------------------------");
            // 上一步任务确认下架后,判断是否有agv拣货类型的货位并执行agv上架指令
            String[] lineIds2 = confirmParamsVO.getLineIds();
            List<WmsMoveD> findByIds = this.moveDMapper.findByIds(WmsMoveD.class, lineIds2, null);
            if (!ObjectUtils.isEmpty(findByIds)) {
                // 获取货位的相关拣货方式
                // 获取库位信息
                String[] locatorIds = Stream.concat(findByIds.stream().map(WmsMoveD::getInLocatorId), findByIds.stream().map(WmsMoveD::getOutLocatorId)).distinct().toArray(String[]::new);
                RestMessage<List<WarehouseLocatorBasicInfo>> locatorResult = warehouseClient.getLocationBasicInfos(locatorIds);
                if (!locatorResult.isSuccess() || CollectionUtil.isEmpty(locatorResult.getData())) {
                    throw new BizException("获取库位信息失败");
                }
                try {
                    log.info("移库涉及货位集合->" + JsonUtils.toString(locatorResult.getData()));
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
                // 加入拣货方式
                for (WmsMoveD wmsMoveD : findByIds) {
                    String inLocatorId = wmsMoveD.getInLocatorId();
                    log.info("移入货位id->" + inLocatorId);
                    WarehouseLocatorBasicInfo inLocator = locatorResult.getData().stream().filter(x -> x.getWarehouseLocatorId().equals(wmsMoveD.getInLocatorId())).findFirst().orElse(null);
                    if (inLocator == null) {
                        throw new BizException("移入库位不存在或已失效");
                    } else {
                        String pickMode1 = inLocator.getPickMode();
                        wmsMoveD.setInPickMode(pickMode1);
                    }

                    String outLocatorId = wmsMoveD.getOutLocatorId();
                    log.info("移出id货位->" + outLocatorId);
                    WarehouseLocatorBasicInfo outLocator = locatorResult.getData().stream().filter(x -> x.getWarehouseLocatorId().equals(wmsMoveD.getOutLocatorId())).findFirst().orElse(null);
                    if (outLocator == null) {
                        throw new BizException("移出库位不存在或已失效");
                    } else {
                        String pickMode2 = outLocator.getPickMode();
                        wmsMoveD.setOutPickMode(pickMode2);
                    }
                }

                // 根据头表分组
                Map<String, List<WmsMoveD>> collect = findByIds.stream().filter(p -> !StringUtils.isEmpty(p.getMoveHeaderId())).collect(Collectors.groupingBy(WmsMoveD::getMoveHeaderId));
                if (!ObjectUtils.isEmpty(collect)) {
                    for (List<WmsMoveD> moveDListVOList : collect.values()) {
                        if (!ObjectUtils.isEmpty(moveDListVOList)) {
                            WmsMoveD moveDListVO = moveDListVOList.get(0);
                            String moveHeaderId = moveDListVO.getMoveHeaderId();
                            WmsMoveH moveH = moveHMapper.findById(moveHeaderId, WmsMoveH.class);
                            // 只要是erp审核通过的状态,都需要发送agv上架命令
                            excuteTransferIn(moveDListVOList, moveH);
                        }
                    }
                }
            }
        }
    }

    private void excuteTransferIn(List<WmsMoveD> moveDListVOList, WmsMoveH moveH) {
        // 执行agv上架指令
        List<WmsMoveD> collect = moveDListVOList.stream().filter(p -> !StringUtils.isEmpty(p.getInPickMode()) && !StringUtils.isEmpty(p.getInPickMode()) && p.getInPickMode().toUpperCase().equals(PICK_MODE_DEVICE.toUpperCase())).collect(Collectors.toList());
        if (!ObjectUtils.isEmpty(collect)) {
            // 执行下架操作(对应设备agv出库表)
            excuteTransferAgvIn(collect, moveH);
        }
    }

    private void excuteTransferAgvIn(List<WmsMoveD> collect, WmsMoveH moveH) {
        Map<String, Object> inMap = covertMoveD2AgvInMap(collect, moveH);

        IntegrationSearchData sd = new IntegrationSearchData();
        sd.setInterfaceId(instockTransforId);
        sd.setData(inMap);
        try {
            String string = JsonUtils.toString(sd);
            log.info("移库上架架发送agv指令数据->" + string);
            String searchForJsonStr = integrationApi.searchForJsonStr(sd);
            searchForJsonStr = StringUtils.replaceBlank(searchForJsonStr);
            JsonNode parse = JsonUtils.parse(searchForJsonStr);
            if (parse.has("success") && !parse.get("success").asBoolean()) {
                log.error("移库上架执行agv指令失败!原因->" + searchForJsonStr);
                throw new BizException("移库上架执行agv指令失败!原因->" + searchForJsonStr);
            }
        } catch (Exception e) {
            log.error("移库上架发送agv指令失败!原因->" + e.getMessage());
            throw new BizException("移库上架发送agv指令失败!原因->" + e.getMessage());
        }
        log.info("移库上架架发送agv指令成功!更新任务状态为agv移库上架状态----------------------------");
        // 更新此WmsMoveD状态为agv上架状态
        Set<String> collect2 = collect.stream().map(WmsMoveD::getMoveLineId).collect(Collectors.toSet());
        WmsMoveD dEntity = new WmsMoveD();
        dEntity.setStatus(WmsMoveDStatus.WMS_MOVE_IN_AGV.getStatus());
        Criteria criteria = Criteria.forClass(WmsMoveD.class);
        criteria.restrictionBuilder().in("moveLineId", collect2.toArray(new String[collect2.size()]));
        criteria.buildRestriction();
        moveDMapper.updateFieldsByCriteria(dEntity, FieldSelector.newInstance().include("status"), criteria);
    }

    /**
     * 转化为上架需要字段
     *
     * @param collect
     * @param moveH
     * @return
     */
    private Map<String, Object> covertMoveD2AgvInMap(List<WmsMoveD> collect, WmsMoveH moveH) {
        if (ObjectUtils.isNull(moveH) || ObjectUtils.isNull(collect)) {
            throw new BizException("移库数据为空!无法发送agv指令.请刷新重试!");
        }
        Map<String, Object> resultData = new HashMap<String, Object>();
        Map<String, Object> headMap = new HashMap<String, Object>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date now = new Date();
        String format = sdf.format(now);
        String DJBH = moveH.getMoveHeaderId(); // 订单编号
        String DWID = moveH.getCargoOwnerId(); // 单位id
        String RQ = format; // 日期
        String RY_KPY = moveH.getModifierName(); // 开票员
        String commentsH = moveH.getComments();

        headMap.put("djbh", DJBH);
        headMap.put("rq", format);
        headMap.put("dwid", DWID); // 没得单位,用货主代替
        headMap.put("ry_cgy", RY_KPY);
        headMap.put("ry_czy", RY_KPY);
        headMap.put("ry_shouhy", RY_KPY);
        headMap.put("ry_zjy", RY_KPY);
        headMap.put("shr", RY_KPY);
        headMap.put("sf_wc", "0");
        headMap.put("sf_xc", "0");
        headMap.put("sf_zx", "0");
        headMap.put("djbh_sj", DJBH);
        headMap.put("rktype", "1"); // 移库
        headMap.put("yzid", DWID);
        headMap.put("bz", commentsH);
        int row = 0;
        List<Map> data_itemlist = new ArrayList<>();
        for (WmsMoveD detail : collect) {
            row++;
            Integer dj_sort = row;
            String itemId = detail.getItemId();
            BigDecimal pieceQty = detail.getPieceQuantity();
            BigDecimal conversionRate = detail.getCaseConversionRate();
            // 数据准确性计算
            if (conversionRate == null) {
                conversionRate = BigDecimal.ONE;
            }
            if (pieceQty == null) {
                pieceQty = BigDecimal.ZERO;
            }
            BigDecimal oddCaseQty = pieceQty.divide(conversionRate, 0, RoundingMode.DOWN); // 整件
            BigDecimal multiply = oddCaseQty.multiply(conversionRate);
            BigDecimal oddPieceQty = pieceQty.subtract(multiply); // 零散
            BigDecimal qty = pieceQty.divide(conversionRate, 4, RoundingMode.DOWN); // 包装总量
            String manufacturerLotNum = detail.getManufacturerLotNum();
            Date batchProductionDate = detail.getBatchProductionDate();
            String convertStringDate1 = Convert.convertStringDate(batchProductionDate);
            Date batchExpiredDate = detail.getBatchExpiredDate();
            String convertStringDate2 = Convert.convertStringDate(batchExpiredDate);
            String sf_zx = "0"; // 是否执行
            String batchId = detail.getBatchId();
            String yspd = "1";// 结论默认合格
            String cgdd_sort = detail.getMoveLineId();
            Integer sl_ywc = 0;
            Integer dj_sort_sj = 1;
            String bz = detail.getComments();
            String task_id = detail.getMoveLineId();
            String containerCode = detail.getInContainerCode();

            Map<String, Object> table = new HashMap<>();
            table.put("djbh", DJBH);
            table.put("tptm", containerCode);
            table.put("dj_sort", dj_sort);
            table.put("spid", itemId);
            table.put("sl", pieceQty);
            table.put("js", oddCaseQty);
            table.put("lss", oddPieceQty);
            table.put("ph", manufacturerLotNum);
            table.put("rq_sc", convertStringDate1);
            table.put("yxqz", convertStringDate2);
            table.put("sf_zx", sf_zx);
            table.put("pch", batchId);
            table.put("yspd", yspd);
            table.put("cgdd_sort", cgdd_sort);
            table.put("sl_ywc", sl_ywc);
            table.put("yzid", DWID);
            table.put("dj_sort_sj", dj_sort_sj);
            table.put("bz", bz);
            table.put("djbh", DJBH);
            table.put("task_id", task_id);

            data_itemlist.add(table);

        }
        headMap.put("data_itemlist", data_itemlist);
        resultData.put("resultData", headMap);
        return resultData;

    }

    @Transactional(rollbackFor = Exception.class)
    public Set<String> genOmsStockAdjustResult(List<MoveSyncInfoVO> syncInfoList, UserDTO user) {
        Set<String> syncIds = new HashSet<>();
        String[] warehouseIds = syncInfoList.stream().map(MoveSyncInfoVO::getWarehouseId).toArray(String[]::new);
        RestMessage<List<BaseWmsWarehouseMst>> warehouseResult = warehouseClient.getWarehouseBasicInfo(warehouseIds);

        String[] bldgIds = syncInfoList.stream().map(MoveSyncInfoVO::getMoveOutBldg).distinct().toArray(String[]::new);
        RestMessage<List<WmsWhBldgDTO>> bldgResult = warehouseClient.getBldgBasicInfoList(bldgIds);

        // 获取货位的相关拣货方式
        // 获取库位信息
        String[] locatorIds = Stream.concat(syncInfoList.stream().map(MoveSyncInfoVO::getInLocatorId), syncInfoList.stream().map(MoveSyncInfoVO::getOutLocatorId)).distinct().toArray(String[]::new);
        RestMessage<List<WarehouseLocatorBasicInfo>> locatorResult = warehouseClient.getLocationBasicInfos(locatorIds);
        if (!locatorResult.isSuccess() || CollectionUtil.isEmpty(locatorResult.getData())) {
            throw new BizException("获取库位信息失败");
        }

        Set<String> cargoOwnerIds = syncInfoList.stream().map(MoveSyncInfoVO::getCargoOwnerId).collect(Collectors.toSet());
        RestMessage<Map<String, BaseCargoOwnerMst>> cargoOwnerResult = chainResource.getCargoOwerByIds(cargoOwnerIds);

        String[] itemIds = syncInfoList.stream().map(MoveSyncInfoVO::getItemId).distinct().toArray(String[]::new);
        RestMessage<List<ItemBaseInfoVO>> itemResult = itemResource.getItemBaseList(itemIds);

        List<OmsStockAdjustmentResultVO> resultListVO = new ArrayList<>();

        List<OmsStockAdjustmentResult> adjustmentResults = syncInfoList.stream().map(sync -> {
            OmsStockAdjustmentResult adjustmentResult = new OmsStockAdjustmentResult();
            if (sync.getMoveBillType().equalsIgnoreCase(WmsMoveBillType.ERP.name())) {
                adjustmentResult.setLineid(sync.getMoveLineId());
                adjustmentResult.setBillid(sync.getMoveHeaderId());
                adjustmentResult.setBillno(sync.getAutoBillNum());
                //				adjustmentResult.setLineid(sync.getLineId());
                //				adjustmentResult.setBillid(sync.getBillId());
                //				adjustmentResult.setBillno(sync.getBillNo());
                adjustmentResult.setRefLineid(sync.getLineId());
                adjustmentResult.setRefBillid(sync.getBillId());
                adjustmentResult.setRefBillno(sync.getBillNo());
            } else {
                adjustmentResult.setLineid(sync.getMoveLineId());
                adjustmentResult.setBillid(sync.getMoveHeaderId());
                adjustmentResult.setBillno(sync.getAutoBillNum());
            }
            adjustmentResult.setSeqno(String.valueOf(sync.getLineNum()));
            adjustmentResult.setDatasource(sync.getMoveBillType().equalsIgnoreCase(WmsMoveBillType.ERP.name()) ? "2" : "1");
            adjustmentResult.setBilldate(DateUtil.beginOfDay(sync.getMoveDate()));
            // 判断是移库类型
            if (sync.getMoveBillType().equals(WmsMoveBillType.CHECK.name())) {
                adjustmentResult.setBilltype(OmsMoveBillType.WMSTOERP_WAITCHECK.name());
            } else {
                adjustmentResult.setBilltype(OmsMoveBillType.WMSTOERP_MOVE.name());
            }
            adjustmentResult.setBranchid(sync.getWarehouseId());
            if (warehouseResult.isSuccess() && CollectionUtil.isNotEmpty(warehouseResult.getData())) {
                BaseWmsWarehouseMst warehouse = warehouseResult.getData().stream().filter(wh -> wh.getWarehouseId().equals(sync.getWarehouseId())).findFirst().orElse(null);
                if (warehouse != null) {
                    adjustmentResult.setBranchname(warehouse.getWarehouseName());
                    adjustmentResult.setBranchcode(warehouse.getWarehouseCode());
                }
            }
            adjustmentResult.setConsignorid(sync.getCargoOwnerId());
            if (cargoOwnerResult.isSuccess() && MapUtil.isNotEmpty(cargoOwnerResult.getData())) {
                BaseCargoOwnerMst cargo = cargoOwnerResult.getData().get(sync.getCargoOwnerId());
                if (cargo != null) {
                    adjustmentResult.setConsignorname(cargo.getCargoOwnerName());
                    adjustmentResult.setAttrStr10(cargo.getCargoOwnerCode());
                }
            }
            adjustmentResult.setIarDate(DateUtil.format(DateUtil.date(), DatePattern.NORM_MONTH_PATTERN));
            adjustmentResult.setItemid(sync.getItemId());
            if (itemResult.isSuccess() && CollectionUtil.isNotEmpty(itemResult.getData())) {
                ItemBaseInfoVO item = itemResult.getData().stream().filter(i -> i.getItemId().equals(sync.getItemId())).findFirst().orElse(null);
                if (item != null) {
                    adjustmentResult.setItemcode(item.getItemCode());
                    adjustmentResult.setItemname(item.getItemName());
                }
            }

            if (bldgResult.isSuccess() && CollectionUtil.isNotEmpty(bldgResult.getData())) {
                WmsWhBldgDTO bldgDTO = bldgResult.getData().stream().filter(b -> b.getBldgId().equals(sync.getMoveOutBldg())).findFirst().orElse(null);
                if (bldgDTO != null) {
                    adjustmentResult.setBuilding(bldgDTO.getBldgCode());
                }
            }

            adjustmentResult.setLotNo(sync.getManufacturerLotNum());
            adjustmentResult.setProductionDate(sync.getBatchProductionDate());
            adjustmentResult.setExpiredDate(sync.getBatchExpiredDate());
            if (ObjectUtils.isNotNull(sync.getBatchProductionDate()) && StrUtil.isEmpty(sync.getBatchProductionDateShow())) {
                adjustmentResult.setAttrStr2(DateUtil.format(sync.getBatchProductionDate(), DatePattern.PURE_DATE_FORMAT));
            } else {
                adjustmentResult.setAttrStr2(sync.getBatchProductionDateShow());
            }
            if (ObjectUtils.isNotNull(sync.getBatchExpiredDate()) && StrUtil.isEmpty(sync.getBatchExpiredDateShow())) {
                adjustmentResult.setAttrStr3(DateUtil.format(sync.getBatchExpiredDate(), DatePattern.PURE_DATE_FORMAT));
            } else {
                adjustmentResult.setAttrStr3(sync.getBatchExpiredDateShow());
            }

            // 加入库别数据
            WarehouseLocatorBasicInfo inLocator = locatorResult.getData().stream().filter(x -> x.getWarehouseLocatorId().equals(sync.getInLocatorId())).findFirst().orElse(null);
            if (inLocator == null) {
                throw new BizException("移入库位不存在或已失效");
            } else {
                adjustmentResult.setAttrStr4(inLocator.getWarehouseAreaCategory());
            }
            WarehouseLocatorBasicInfo outLocator = locatorResult.getData().stream().filter(x -> x.getWarehouseLocatorId().equals(sync.getOutLocatorId())).findFirst().orElse(null);
            if (outLocator == null) {
                throw new BizException("移出库位不存在或已失效");
            } else {
                adjustmentResult.setAttrStr5(outLocator.getWarehouseAreaCategory());
            }

            adjustmentResult.setStockStatus(sync.getInStockStatus());
            adjustmentResult.setShelfLifeDays(0);
            adjustmentResult.setQuantityPre(sync.getOddPieceQuantity());
            adjustmentResult.setQuantityBalance(sync.getOddPieceQuantity());
            adjustmentResult.setQuantity(sync.getOddPieceQuantity());
            adjustmentResult.setAdjustReason(sync.getReason());
            adjustmentResult.setStockStatusOld(sync.getOutStockStatus());
            // 库存调整单据状态0作废10已创建20审核30过账
            adjustmentResult.setStatus("20");
            adjustmentResult.setCreateby(user.getId());
            adjustmentResult.setCreatedate(DateUtil.date());
            adjustmentResult.setLastmodifyby(user.getId());
            adjustmentResult.setLastmodifydate(DateUtil.date());
            adjustmentResult.setPEnterpriseid(user.getEnterpriseId());
            adjustmentResult.setPEnterprisename(user.getEnterpriseName());
            adjustmentResult.setWmsbillno(sync.getAutoBillNum());
            adjustmentResult.setBatchId(sync.getBatchId());
            adjustmentResult.setBatchNum(sync.getBatchNum());

            syncIds.add(adjustmentResult.getBillid());
            //库存状态变化，调用OMS 通过接口平台
            if(!sync.getInStockStatus().equals(sync.getOutStockStatus())){
                OmsStockAdjustmentResultVO resultVO = new OmsStockAdjustmentResultVO();
                resultVO.setBillno(adjustmentResult.getBillno());
                resultVO.setItemcode(adjustmentResult.getItemcode());
                resultVO.setQty(new BigDecimal(adjustmentResult.getQuantity()));
                resultVO.setSourceqty(adjustmentResult.getStockStatusOld());
                resultVO.setNewStockStatus(adjustmentResult.getStockStatus());
                resultVO.setUpdateDate(adjustmentResult.getBilldate()==null?null:DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss",adjustmentResult.getBilldate()));
                resultVO.setLotNo(adjustmentResult.getLotNo());
                resultVO.setPdateFrom(adjustmentResult.getProductionDate()==null?null:DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss",adjustmentResult.getProductionDate()));
                resultVO.setValidUntil(adjustmentResult.getExpiredDate()==null?null:DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss",adjustmentResult.getExpiredDate()));
                resultVO.setConsignorcode(adjustmentResult.getAttrStr10());
                resultVO.setBranchcode(adjustmentResult.getBranchcode());
                resultVO.setPenterpriseid(adjustmentResult.getPEnterpriseid());
                resultVO.setResource(adjustmentResult.getAdjustReason());
                resultVO.setInformationby(adjustmentResult.getCreateby());
                resultVO.setAdjustType("move");
                resultVO.setRemark(adjustmentResult.getRemark());
                resultVO.setItemid(adjustmentResult.getItemid());
                resultListVO.add(resultVO);
            }

            return adjustmentResult;
        }).collect(Collectors.toList());

        adjustmentResultMapper.saveBatch(adjustmentResults);

        try {
            if(resultListVO.size()>0){
                if (StringUtils.isEmpty(omsStockAdjustmentResultUrl)) {
                    throw new BizException("请求路径配置为空");
                }
                System.out.println(JSON.toJSONString(resultListVO));
                String url = omsStockAdjustmentResultUrl;
                String result = HttpUtil.post(url, JSONObject.toJSONString(resultListVO));
                JSONObject parse = JSONObject.parseObject(result);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        return syncIds;
    }

    @Override
    public Page<MoveListVO> getMoveList(MoveQueryVO moveQueryDVO) {
        UserDTO user = userService.getUser();
        if (StringUtils.isBlank(moveQueryDVO.getSubBillType())) {
            throw new BizException("移动类型不能为空");
        }
        if (StringUtils.isBlank(moveQueryDVO.getStatus())) {
            throw new BizException("请选择移库状态");
        }
        WmsMoveSubBillType wmsMoveSubBillType = WmsMoveSubBillType.get(moveQueryDVO.getSubBillType());
        if (wmsMoveSubBillType == null) {
            throw new BizException("未知的移动类型");
        }

        if (StringUtils.isNotEmpty(moveQueryDVO.getMoveBillType())) {
            WmsMoveBillType moveBillType = WmsMoveBillType.get(moveQueryDVO.getMoveBillType());
            if (moveBillType == null) {
                throw new BizException("未知的来源类型");
            }
        }

        if (moveQueryDVO.getEnd() != null) {
            moveQueryDVO.setEnd(DateUtil.endOfDay(moveQueryDVO.getEnd()));
        }
        Criteria criteria = Criteria.forClass(WmsMoveH.class);
        criteria.restrictionBuilder().eq("enterpriseId", user.getEnterpriseId()).eq("subBillType", wmsMoveSubBillType.getType()).eq("status", moveQueryDVO.getStatus());
        if (StrUtil.isNotBlank(moveQueryDVO.getMoveBillType())) {
            criteria.restrictionBuilder().eq("moveBillType", moveQueryDVO.getMoveBillType());
        }

        if (StrUtil.isNotBlank(moveQueryDVO.getCargoOwnerId())) {
            criteria.restrictionBuilder().eq("cargoOwnerId", moveQueryDVO.getCargoOwnerId());
        }

        if (StrUtil.isNotBlank(moveQueryDVO.getWarehouseId())) {
            criteria.restrictionBuilder().eq("warehouseId", moveQueryDVO.getWarehouseId());
        }

        if (StrUtil.isNotBlank(moveQueryDVO.getMoveHeaderId())) {
            criteria.restrictionBuilder().likeAll("moveHeaderId", moveQueryDVO.getMoveHeaderId());
        }

        if (StrUtil.isNotBlank(moveQueryDVO.getAutoBillNum())) {
            criteria.restrictionBuilder().eq("autoBillNum", moveQueryDVO.getAutoBillNum());
        }

        if (StrUtil.isNotBlank(moveQueryDVO.getMoveInBldg())) {
            criteria.restrictionBuilder().eq("moveOutBldg", moveQueryDVO.getMoveInBldg());
        }

        if (StrUtil.isNotBlank(moveQueryDVO.getMoveOutBldg())) {
            criteria.restrictionBuilder().eq("moveInBldg", moveQueryDVO.getMoveOutBldg());
        }

        if (ObjectUtil.isNotNull(moveQueryDVO.getStart())) {
            criteria.restrictionBuilder().gt("moveDate", moveQueryDVO.getStart());
        }

        if (ObjectUtil.isNotNull(moveQueryDVO.getEnd())) {
            criteria.restrictionBuilder().lt("moveDate", moveQueryDVO.getEnd());
        }
        criteria.orderDescBy("moveDate");
        criteria.buildRestriction();
        Page<WmsMoveH> hList = PageUtils.getPage(moveHMapper.findByCriteria(criteria));

        if (CollectionUtil.isEmpty(hList.getList())) {
            return null;
        }

        // 组合id
        Set<String> cargoOwnerIds = hList.getList().stream().map(WmsMoveH::getCargoOwnerId).collect(Collectors.toSet());
        RestMessage<Map<String, BaseCargoOwnerMst>> cargoResult = chainResource.getCargoOwerByIds(cargoOwnerIds);

        String[] bldgIds = Stream.concat(hList.getList().stream().map(WmsMoveH::getMoveInBldg), hList.getList().stream().map(WmsMoveH::getMoveOutBldg)).distinct().toArray(String[]::new);
        RestMessage<List<WmsWhBldgDTO>> bldgResult = warehouseClient.getBldgBasicInfoList(bldgIds);
        return BeanConvertUtils.convertPageTo(hList, MoveListVO::new, (s, t) -> {
            if (cargoResult.isSuccess() && MapUtil.isNotEmpty(cargoResult.getData())) {
                BaseCargoOwnerMst cargo = cargoResult.getData().get(t.getCargoOwnerId());
                if (cargo != null) {
                    t.setCargoOwnerName(cargo.getCargoOwnerName());
                }
            }
            if (bldgResult.isSuccess() && CollectionUtil.isNotEmpty(bldgResult.getData())) {
                WmsWhBldgDTO inBldg = bldgResult.getData().stream().filter(x -> x.getBldgId().equals(t.getMoveInBldg())).findFirst().orElse(null);
                if (inBldg != null) {
                    t.setMoveInBldgName(inBldg.getBldgName());
                }

                WmsWhBldgDTO outBldg = bldgResult.getData().stream().filter(x -> x.getBldgId().equals(t.getMoveOutBldg())).findFirst().orElse(null);
                if (outBldg != null) {
                    t.setMoveOutBldgName(outBldg.getBldgName());
                }
            }
            t.setExcuteUser(s.getModifierName());
        });
    }

    @Override
    public Page<MoveVO> getMoveDetailList(String headerId, String status) {
        WmsMoveDStatus dStatus = null;
        if (StrUtil.isNotBlank(status)) {
            dStatus = WmsMoveDStatus.get(status);
            if (dStatus == null) {
                throw new BizException("请选择正确的移库状态");
            }
        }
        UserDTO user = userService.getUser();
        Criteria criteria = Criteria.forClass(WmsMoveD.class);
        criteria.restrictionBuilder().eq("enterpriseId", user.getEnterpriseId()).eq("moveHeaderId", headerId);
        if (dStatus != null) {
            criteria.restrictionBuilder().eq("status", dStatus.getStatus());
        }
        criteria.orderDescBy("gmtCreate");
        criteria.buildRestriction();
        Page<WmsMoveD> dList = PageUtils.getPage(moveDMapper.findByCriteria(criteria));
        if (CollectionUtil.isEmpty(dList.getList())) {
            return null;
        }
        // 组合id
        String[] itemIds = dList.getList().stream().map(WmsMoveD::getItemId).distinct().toArray(String[]::new);

        // 请求商品信息
        RestMessage<List<ItemBaseInfoVO>> itemResult = itemResource.getItemBaseList(itemIds);

        // 组合id
        String[] locatorIds = Stream.concat(dList.getList().stream().map(WmsMoveD::getOutLocatorId), dList.getList().stream().map(WmsMoveD::getInLocatorId)).distinct().toArray(String[]::new);

        // 请求货位信息
        RestMessage<List<WarehouseLocatorBasicInfo>> locatorResult = warehouseClient.getLocationBasicInfos(locatorIds);

        // 组合id
        String[] userIds = dList.getList().stream().map(WmsMoveD::getWarehouseWorkerId).distinct().toArray(String[]::new);
        // 请求用户信息
        RestMessage<List<UserBasicVO>> userResult = platformPowerClient.getEnterpriseUserByUserIds(userIds);

        return BeanConvertUtils.convertPageTo(dList, MoveVO::new, (s, t) -> {
            if (itemResult.isSuccess() && CollectionUtil.isNotEmpty(itemResult.getData())) {
                Optional<ItemBaseInfoVO> optional = itemResult.getData().stream().filter(y -> y.getItemId().equals(t.getItemId())).findFirst();
                optional.ifPresent(item -> {
                    t.setItemCode(item.getItemCode());
                    t.setItemName(item.getItemName());
                    t.setUom(item.getCaseUom());
                    t.setManufacturerId(item.getManufacturerId());
                    t.setManufacturerName(item.getManufacturerId());
                    t.setCaseConversionRate(item.getCaseConversionRate());
                    t.setSpecification(item.getSpecification());
                });
            }

            if (locatorResult.isSuccess() && CollectionUtil.isNotEmpty(locatorResult.getData())) {
                Optional<WarehouseLocatorBasicInfo> optionalIn = locatorResult.getData().stream().filter(y -> y.getWarehouseLocatorId().equals(t.getInLocatorId())).findFirst();
                optionalIn.ifPresent(locator -> {
                    t.setInWarehouseAreaCategory(locator.getWarehouseAreaCategory());
                    t.setInLocatorName(locator.getWarehouseLocatorName());
                    t.setInLocatorCode(locator.getWarehouseLocatorCode());
                    t.setInAreaCode(locator.getWarehouseAreaCode());
                });

                Optional<WarehouseLocatorBasicInfo> optionalOut = locatorResult.getData().stream().filter(y -> y.getWarehouseLocatorId().equals(t.getOutLocatorId())).findFirst();
                optionalOut.ifPresent(locator -> {
                    t.setOutWarehouseAreaCategory(locator.getWarehouseAreaCategory());
                    t.setOutLocatorName(locator.getWarehouseLocatorName());
                    t.setOutLocatorCode(locator.getWarehouseLocatorCode());
                    t.setOutAreaCode(locator.getWarehouseAreaCode());
                });
            }

            if (userResult.isSuccess() && userResult.getData() != null) {
                Optional<UserBasicVO> optional = userResult.getData().stream().filter(y -> y.getId().equals(t.getWarehouseWorkerId())).findFirst();
                optional.ifPresent(employee -> t.setWarehouseWorkerName(employee.getNickname()));
            }
        });
    }

    @Override
    public Page<MoveDListVO> getMoveDetailList(MoveQueryDVO moveQueryDVO) {
        UserDTO user = userService.getUser();
        WmsMoveSubBillType wmsMoveSubBillType = WmsMoveSubBillType.get(moveQueryDVO.getSubBillType());
        if (StringUtils.isBlank(moveQueryDVO.getSubBillType())) {
            throw new BizException("移动类型不能为空");
        }
        if (wmsMoveSubBillType == null) {
            throw new BizException("未知的移动类型");
        }

        if (StringUtils.isNotEmpty(moveQueryDVO.getMoveBillType())) {
            WmsMoveBillType moveBillType = WmsMoveBillType.get(moveQueryDVO.getMoveBillType());
            if (moveBillType == null) {
                throw new BizException("未知的来源类型");
            }
        }

        if (moveQueryDVO.getEnd() != null) {
            moveQueryDVO.setEnd(DateUtil.endOfDay(moveQueryDVO.getEnd()));
        }

        Page<MoveDListVO> vos = PageUtils.getPage(moveDMapper.selectMoveDInfoList(user.getEnterpriseId(), moveQueryDVO));

        if (CollectionUtil.isEmpty(vos.getList())) {
            return null;
        }
        // 填充移库任务数据
        this.buildMoveList(vos.getList());
        return vos;
    }

    private void buildMoveList(List<MoveDListVO> vos) {
        // 组合id
        String[] itemIds = vos.stream().map(MoveDListVO::getItemId).distinct().toArray(String[]::new);

        // 请求商品信息
        RestMessage<List<ItemBaseInfoVO>> itemResult = itemResource.getItemBaseList(itemIds);

        // 组合id
        Set<String> cargoOwnerIds = vos.stream().map(MoveDListVO::getCargoOwnerId).collect(Collectors.toSet());
        RestMessage<Map<String, BaseCargoOwnerMst>> cargoResult = chainResource.getCargoOwerByIds(cargoOwnerIds);

        // 组合id
        String[] warehouseIds = vos.stream().map(MoveDListVO::getWarehouseId).distinct().toArray(String[]::new);

        // 请求仓库信息
        RestMessage<List<BaseWmsWarehouseMst>> warehouseResult = warehouseClient.getWarehouseBasicInfo(warehouseIds);

        // 组合id
        String[] locatorIds = Stream.concat(vos.stream().map(MoveDListVO::getOutLocatorId), vos.stream().map(MoveDListVO::getInLocatorId)).distinct().toArray(String[]::new);

        // 请求货位信息
        RestMessage<List<WarehouseLocatorBasicInfo>> locatorResult = warehouseClient.getLocationBasicInfos(locatorIds);

        // 组合id
        String[] batchIds = vos.stream().map(MoveDListVO::getBatchId).distinct().toArray(String[]::new);
        // 请求库存批次信息
        RestMessage<List<BatchNumberListVO>> stockResult = stockResource.getBatchNumberBasicInfo(batchIds);
        // 组合id
        String[] userIds = vos.stream().map(MoveDListVO::getWarehouseWorkerId).distinct().toArray(String[]::new);
        // 请求用户信息
        RestMessage<List<UserBasicVO>> userResult = platformPowerClient.getEnterpriseUserByUserIds(userIds);

        // 组合id
        String[] bldgIds = Stream.concat(vos.stream().map(MoveDListVO::getMoveInBldg), vos.stream().map(MoveDListVO::getMoveOutBldg)).distinct().toArray(String[]::new);
        RestMessage<List<WmsWhBldgDTO>> bldgResult = warehouseClient.getBldgBasicInfoList(bldgIds);

        vos.forEach(x -> {
            if (itemResult.isSuccess() && itemResult.getData() != null) {
                Optional<ItemBaseInfoVO> optional = itemResult.getData().stream().filter(y -> y.getItemId().equals(x.getItemId())).findFirst();
                optional.ifPresent(itemBaseInfoVO -> {
                    x.setItemCode(itemBaseInfoVO.getItemCode());
                    x.setItemName(itemBaseInfoVO.getItemName());
                    x.setManufacturerId(itemBaseInfoVO.getManufacturerId());
                    x.setManufacturerName(itemBaseInfoVO.getManufacturerName());
                    x.setCountryOriginId(itemBaseInfoVO.getCountryOriginId());
                    x.setItemHealthCategory(itemBaseInfoVO.getItemHealthCategory());
                    x.setItemHealthListedLicensor(itemBaseInfoVO.getItemHealthListedLicensor());
                    x.setItemHealthProduceLicense(itemBaseInfoVO.getItemHealthProduceLicense());
                    x.setItemHealthRegisterNbr(itemBaseInfoVO.getItemHealthRegisterNbr());
                    x.setItemHealthApprovalNbr(itemBaseInfoVO.getItemHealthApprovalNbr());
                    x.setItemHealthRecordNbr(itemBaseInfoVO.getItemHealthRecordNbr());
                    x.setItemAssistantCode(itemBaseInfoVO.getItemAssistantCode());
                    x.setIsCaseOrPiece(itemBaseInfoVO.getIsCaseOrPiece());
                    x.setItemHealthSuperviseflag(itemBaseInfoVO.getItemHealthSuperviseflag());
                    x.setItemIsValuable(itemBaseInfoVO.getItemIsValuable());
                    x.setItemIsFragile(itemBaseInfoVO.getItemIsFragile());
                    x.setIsBacteria(itemBaseInfoVO.getIsBacteria());
                    x.setIsDelegate(itemBaseInfoVO.getIsDelegate());
                    x.setIsHosted(itemBaseInfoVO.getIsHosted());
                    x.setUom(itemBaseInfoVO.getCaseUom());
                    x.setVolume(String.valueOf(itemBaseInfoVO.getVolume()));
                    x.setWeight(String.valueOf(itemBaseInfoVO.getWeight()));
                });
            }

            if (cargoResult.isSuccess() && cargoResult.getData() != null) {
                BaseCargoOwnerMst cargoOwnerMst = cargoResult.getData().get(x.getCargoOwnerId());
                if (cargoOwnerMst != null) {
                    x.setCargoOwnerName(cargoOwnerMst.getCargoOwnerName());
                }
            }

            if (locatorResult.isSuccess() && locatorResult.getData() != null) {
                Optional<WarehouseLocatorBasicInfo> optionalIn = locatorResult.getData().stream().filter(y -> y.getWarehouseLocatorId().equals(x.getInLocatorId())).findFirst();
                optionalIn.ifPresent(locator -> {
                    x.setInWarehouseAreaCategory(locator.getWarehouseAreaCategory());
                    x.setInLocatorName(locator.getWarehouseLocatorName());
                    x.setInLocatorCode(locator.getWarehouseLocatorCode());
                    x.setInWarehouseAreaId(locator.getWarehouseAreaId());
                });

                Optional<WarehouseLocatorBasicInfo> optionalOut = locatorResult.getData().stream().filter(y -> y.getWarehouseLocatorId().equals(x.getOutLocatorId())).findFirst();
                optionalOut.ifPresent(locator -> {
                    x.setOutWarehouseAreaCategory(locator.getWarehouseAreaCategory());
                    x.setOutLocatorName(locator.getWarehouseLocatorName());
                    x.setOutLocatorCode(locator.getWarehouseLocatorCode());
                });
            }

            if (bldgResult.isSuccess() && bldgResult.getData() != null) {
                Optional<WmsWhBldgDTO> optionalIn = bldgResult.getData().stream().filter(y -> y.getBldgId().equals(x.getMoveInBldg())).findFirst();
                optionalIn.ifPresent(bldg -> x.setMoveInBldgName(bldg.getBldgName()));
                Optional<WmsWhBldgDTO> optionalOut = bldgResult.getData().stream().filter(y -> y.getBldgId().equals(x.getMoveOutBldg())).findFirst();
                optionalOut.ifPresent(bldg -> x.setMoveOutBldgName(bldg.getBldgName()));
            }

            if (warehouseResult.isSuccess() && warehouseResult.getData() != null) {
                Optional<BaseWmsWarehouseMst> optional = warehouseResult.getData().stream().filter(y -> y.getWarehouseId().equals(x.getWarehouseId())).findFirst();
                optional.ifPresent(warehouse -> x.setWarehouseName(warehouse.getWarehouseName()));
            }
            if (stockResult.isSuccess() && stockResult.getData() != null) {
                Optional<BatchNumberListVO> optional = stockResult.getData().stream().filter(y -> y.getBatchId().equals(x.getBatchId())).findFirst();
                optional.ifPresent(batchInfo -> {
                    x.setShelfLifeDays(batchInfo.getShelfLifeDays());
                    x.setManufacturerLotNum(batchInfo.getManufacturerLotNum());
                    x.setBatchNum(batchInfo.getBatchNum());
                    x.setBatchProductionDate(batchInfo.getBatchProductionDate());
                    x.setBatchExpiredDate(batchInfo.getBatchExpiredDate());
                    x.setBatchProductionDateShow(batchInfo.getBatchProductionDateShow());
                    x.setBatchExpiredDateShow(batchInfo.getBatchExpiredDateShow());
                    // 加入显示日期字段
                    if (StringUtils.isBlank(batchInfo.getBatchExpiredDateShow())) {
                        x.setBatchProductionDateShow(DateUtil.format(batchInfo.getBatchProductionDate(), "yyyy/MM/dd"));
                    }
                    if (StringUtils.isBlank(batchInfo.getBatchExpiredDateShow())) {
                        x.setBatchProductionDateShow(DateUtil.format(batchInfo.getBatchExpiredDate(), "yyyy/MM/dd"));
                    }

                });
            }

            if (userResult.isSuccess() && userResult.getData() != null) {
                Optional<UserBasicVO> optional = userResult.getData().stream().filter(y -> y.getId().equals(x.getWarehouseWorkerId())).findFirst();
                optional.ifPresent(employee -> x.setWarehouseWorkerName(employee.getNickname()));
            }
        });
    }

    @Override
    public List<MoveDListVO> getMoveDownShelvesTask(MoveQueryDVO moveQueryDVO) {
        UserDTO user = userService.getUser();
        if (null == user) {
            throw new BizException("用户信息获取失败！");
        }
        if (null == moveQueryDVO) {
            throw new BizException("请求入参不能为空！");
        }
        if (StringUtils.isBlank(moveQueryDVO.getOutcontainercode()) && StringUtils.isBlank(moveQueryDVO.getOutgoodscasecode())
                && StringUtils.isBlank(moveQueryDVO.getOutlocatorcode())) {
            throw new BizException("托盘号、货格号、下架货位编码不能同时为空！");
        }
        if (StringUtils.isNotBlank(moveQueryDVO.getOutlocatorcode())) {
            // 根据货位编码查询货位ID
            RestMessage<WarehouseLocatorBasicInfo> locatorResult = warehouseClient.getLocationInfoByCode(user.getUserBizModel().getDefaultWarehouseId(), moveQueryDVO.getOutlocatorcode());
            if (!locatorResult.isSuccess() || null == locatorResult.getData()) {
                throw new BizException("根据货位编码【" + moveQueryDVO.getOutlocatorcode() + "】获取货位信息失败！");
            }
            moveQueryDVO.setOutlocatorid(locatorResult.getData().getWarehouseLocatorId());
        }

        // 默认查询WMS已审核通过的移库单
        moveQueryDVO.setStatusList(new String[]{ WmsMoveDStatus.WMS_AUDIT_PASS.getStatus(), WmsMoveDStatus.ERP_AUDIT_PASS.getStatus() });
        List<MoveDListVO> vos = moveDMapper.selectMoveDInfoList(user.getEnterpriseId(), moveQueryDVO);
        if (CollectionUtil.isEmpty(vos)) {
            throw new BizException("没有查询到对应的移库任务！");
        }
        // 填充数据
        this.buildMoveList(vos);

        return vos;
    }

    @Override
    public List<MoveDListVO> getMoveUpShelvesTask(MoveQueryDVO moveQueryDVO) {
        UserDTO user = userService.getUser();
        if (null == user) {
            throw new BizException("用户信息获取失败！");
        }
        if (null == moveQueryDVO) {
            moveQueryDVO = new MoveQueryDVO();
        }

        // 默认查询WMS已审核通过的移库单
        moveQueryDVO.setStatusList(new String[]{WmsMoveDStatus.WMS_MOVE_OUT.getStatus()});
        List<MoveDListVO> vos = moveDMapper.selectMoveDInfoList(user.getEnterpriseId(), moveQueryDVO);
        if (CollectionUtil.isEmpty(vos)) {
            throw new BizException("没有查询到对应的移库任务！");
        }
        // 填充数据
        this.buildMoveList(vos);

        return vos;
    }

    @Override
    public Page<MoveErpListVO> getErpMoveList(String erpBillId, String erpBillNo, OmsMoveBillType billType) {
        UserDTO user = userService.getUser();
        Page<MoveErpListVO> erpList = PageUtils.getPage(adjustmentRecordMapper.getOmsMoveList(user.getEnterpriseId(), erpBillId, erpBillNo, billType.name()));
        String[] itemIds = erpList.getList().stream().map(MoveErpListVO::getItemId).distinct().toArray(String[]::new);
        RestMessage<List<ItemBaseInfoVO>> itemResult = itemResource.getItemBaseList(itemIds);
        erpList.getList().forEach(x -> {
            ItemBaseInfoVO itemBaseInfoVO = itemResult.getData().stream().filter(i -> i.getItemId().equals(x.getItemId())).findFirst().orElse(null);
            if (itemBaseInfoVO != null) {
                x.setCaseConvertionRate(itemBaseInfoVO.getCaseConversionRate());
                x.setIsCaseOrPiece(itemBaseInfoVO.getIsCaseOrPiece());
            }
        });
        List<MoveErpListVO> list = erpList.getList();
        // 此处需要判断单据类型是否为移库的,若是为放行移库,则要推荐货位
        if (!ObjectUtils.isNull(billType) && billType.equals(OmsMoveBillType.WMSTOERP_WAITCHECK) && !ObjectUtils.isEmpty(list)) {
            // 组装货位推荐所需数据
            recommendLocator(user, list);
        }

        return erpList;
    }

    /**
     * 推荐移库库别以及货位
     *
     * @param user
     * @param list
     */
    private void recommendLocator(UserDTO user, List<MoveErpListVO> list) {

        String enterpriseId = user.getEnterpriseId();
        MoveErpListVO moveErpListVO2 = list.get(0);
        String cargoOwnerId = moveErpListVO2.getCargoOwnerId();
        String warehouseId = moveErpListVO2.getWarehouseId();
        log.info("-------调用策略服务找寻库别");
        RestMessage<StrategyDTO> strategyResult = strategyClient.getStrategyDTO(enterpriseId, "InStock", cargoOwnerId, warehouseId);
        if (strategyResult == null || !strategyResult.isSuccess() || strategyResult.getData() == null) {
            log.info("未找到有效推荐库别的策略,请联系相关人员检查自己的策略!");
        }
        StrategyDTO strategyDTO = strategyResult.getData();
        Strategy strategy = null;
        try {
            log.info("创建上架寻库别策略中-------+" + strategyDTO.toString());
            strategy = strategyDTO.createStrategy();
        } catch (Exception e) {
            // 打印日志
            log.error("策略创建失败,原因->", e);
            //			throw new BizException("未找到有效推荐库别的策略,请联系管理员设置!");
        }
        log.info("-------调用策略服务成功!");
        if (null == strategy) {
            log.info("创建上架策略为空!");
            return;
        }

        // 获取商品数据
        // 组合id
        String[] itemIds = list.stream().map(MoveErpListVO::getItemId).distinct().toArray(String[]::new);

        // 请求商品信息
        RestMessage<List<ItemBaseInfoVO>> itemResult = null;
        try {
            itemResult = itemResource.getItemBaseList(itemIds);
        } catch (Exception e1) {
            log.info("商品查询数据为空,原因->", e1);
        }

        // 远程调用common服务,转义库别编码为名称
        List<BaseCommonDict> commonDictList = null;
        try {
            RestMessage<List<BaseCommonDict>> dictBasicListByCodes = commonResource.getDictBasicListByCodes(areaCategoryDictId, null);
            commonDictList = dictBasicListByCodes.getData();
        } catch (Exception e1) {
            e1.printStackTrace();
        }

        List<LocatorQueryVO> queryVOList = new ArrayList<>();
        for (MoveErpListVO moveErpListVO : list) {
            String lineId = moveErpListVO.getLineId();
            WhAreaCategoryContext context = new WhAreaCategoryContext();
            WhAreaCategoryContext.RcvLineData rcvLineData = context.new RcvLineData();
            // 是否整散
            BigDecimal pieceQty = moveErpListVO.getOddPieceQuantity();
            if (pieceQty == null) {
                pieceQty = BigDecimal.ZERO;
                moveErpListVO.setOddPieceQuantity(pieceQty);
            }
            BigDecimal conversionRate = moveErpListVO.getCaseConvertionRate();
            BigDecimal oddCaseQty = pieceQty.divide(conversionRate, 0, RoundingMode.DOWN);
            BigDecimal oddPieceQty = pieceQty.subtract(oddCaseQty.multiply(conversionRate));
            // 判断数据的整散
            if (null != oddPieceQty && oddPieceQty.compareTo(BigDecimal.ZERO) == 0) {
                // 为整数
                rcvLineData.setIsCase("0");
            }

            if (null != oddCaseQty && oddCaseQty.compareTo(BigDecimal.ZERO) == 0 && null != oddPieceQty && oddPieceQty.compareTo(BigDecimal.ZERO) == 1) {
                // 零散散
                rcvLineData.setIsCase("1");
            }

            if (null != oddCaseQty && oddCaseQty.compareTo(BigDecimal.ZERO) == 1 && null != oddPieceQty && oddPieceQty.compareTo(BigDecimal.ZERO) == 1) {
                // 整散
                rcvLineData.setIsCase("2");
            }
            String inStockStatus = "0";
            //			if (!StringUtils.isBlank(moveErpListVO.getInStockStatus())) {
            //				inStockStatus = moveErpListVO.getInStockStatus();
            //			}
            rcvLineData.setStorageStatus(inStockStatus);
            context.setRcvLineData(rcvLineData);
            try {
                log.info("-------开始执行策略中...............!,策略条件数据->" + JsonUtils.toString(context));
                strategy.execute(context);
            } catch (Exception e) {
                log.info("策略执行失败,原因->", e);
                continue;
            }
            boolean completed = context.completed();
            if (completed) {
                // 找到匹配的策略
                log.info("-------获取合适策略");
                List<String> targetAreaCategoryCodes = context.getTargetAreaCategoryCodes();
                if (!ObjectUtils.isEmpty(targetAreaCategoryCodes)) {
                    String areaCategory = targetAreaCategoryCodes.get(0);
                    log.info(String.format("移库上架任务[%s],推荐库别[%s]", lineId, areaCategory));
                    moveErpListVO.setInAreaCategory(areaCategory);
                    transferred(areaCategory, moveErpListVO, commonDictList);
                    //					moveErpListVO.setDestWarehouseCategoryName(areaCategory);
                } else {
                    log.info(String.format("移库上架任务[%s],推荐库别为空!", lineId));
                    continue;
                }
            }

            LocatorQueryVO vo = new LocatorQueryVO();
            String refId = lineId;
            vo.setRefId(refId);
            vo.setEnterpriseId(enterpriseId);

            // 组装查询货位的条件
            InstockItemInfo info = new InstockItemInfo();
            buildGetLocatorCondition(info, moveErpListVO, warehouseId, cargoOwnerId, enterpriseId, itemResult);
            vo.setItemInfo(info);
            queryVOList.add(vo);
        }

        // 调用货位限定策略获取合适的推荐货位
        excuteRecommendLocator(list, queryVOList);

    }

    private void transferred(String areaCategory, MoveErpListVO moveErpListVO, List<BaseCommonDict> commonDictList) {
        // 转义库别编码
        if (StringUtils.isBlank(areaCategory)) {
            return;
        }
        areaCategory = areaCategory.toUpperCase();
        if (!ObjectUtils.isEmpty(commonDictList)) {
            Map<String, List<BaseCommonDict>> collect = commonDictList.stream().filter(p -> !StringUtils.isBlank(p.getDictCode())).collect(Collectors.groupingBy(p -> p.getDictCode().toUpperCase()));
            if (!ObjectUtils.isEmpty(collect)) {
                List<BaseCommonDict> list = collect.get(areaCategory);
                if (!ObjectUtils.isEmpty(list) && null != list.get(0)) {
                    BaseCommonDict baseCommonDict = list.get(0);
                    moveErpListVO.setDestWarehouseCategoryName(baseCommonDict.getDictName());
                }
            }
        } else {
            String destWarehouseCategoryName = "";
            switch (areaCategory) {
                case BizConstant.ZJK:
                    destWarehouseCategoryName = "整件库";
                    break;
                case BizConstant.LHK:
                    destWarehouseCategoryName = "零货库";
                    break;
                case BizConstant.ZCQ:
                    destWarehouseCategoryName = "暂存区";
                    break;
                case BizConstant.LTK:
                    destWarehouseCategoryName = "立体库";
                    break;
                case BizConstant.BHK:
                    destWarehouseCategoryName = "备货库";
                    break;
                case BizConstant.THK:
                    destWarehouseCategoryName = "退货库";
                    break;
                case BizConstant.NHK:
                    destWarehouseCategoryName = "不合格品库";
                    break;
                case BizConstant.PDK:
                    destWarehouseCategoryName = "盘点库";
                    break;
                case BizConstant.DYK:
                    destWarehouseCategoryName = "待检库";
                    break;
                case BizConstant.HYK:
                    destWarehouseCategoryName = "整散合一库";
                    break;
                case BizConstant.CLK:
                    destWarehouseCategoryName = "待处理库";
                    break;
                case BizConstant.ADJ:
                    destWarehouseCategoryName = "调整库";
                    break;
                case BizConstant.DGK:
                    destWarehouseCategoryName = "代管库";
                    break;
                case BizConstant.YKQ:
                    destWarehouseCategoryName = "越库区";
                    break;
                case BizConstant.KRK:
                    destWarehouseCategoryName = "空入库";
                    break;
                case BizConstant.XTK:
                    destWarehouseCategoryName = "箱体库";
                    break;
                case BizConstant.ZDK:
                    destWarehouseCategoryName = "直调库";
                    break;
                case BizConstant.SBK:
                    destWarehouseCategoryName = "设备库";
                    break;
                default:
                    break;
            }
            moveErpListVO.setDestWarehouseCategoryName(destWarehouseCategoryName);
        }

    }

    private void excuteRecommendLocator(List<MoveErpListVO> list, List<LocatorQueryVO> queryVOList) {
        RestMessage<Map<String, BaseWmsWarehouseLocator>> recommendLocatoror = null;
        try {
            log.info("-------开始调用远程寻货位操作!--入参数:" + JsonUtils.toString(queryVOList));
            recommendLocatoror = controlClient.getRecommendLocatoror(queryVOList);
        } catch (Exception e) {
            log.info(String.format("远程调用库位推荐失败!错误原因->%s", e.getMessage()));
        }
        Map<String, BaseWmsWarehouseLocator> data = null;
        if (!ObjectUtils.isNull(recommendLocatoror) && recommendLocatoror.isSuccess()) {
            data = recommendLocatoror.getData();
            if (ObjectUtils.isNull(data)) {
                log.error(String.format("推荐数据为空!请重试!"));
                return;
            }
            // 看是否有数据
            int row = 0;
            for (Entry<String, BaseWmsWarehouseLocator> entry : data.entrySet()) {
                row++;
                BaseWmsWarehouseLocator value = entry.getValue();
                String key = entry.getKey();
                if (ObjectUtils.isNull(value)) {
                    log.info(String.format("第[%d]行数据,来源id(移库lineId)[%s]的推荐货位为空!", row, key));
                }
            }
            log.info("-------寻货位成功!");

            int row2 = 0;
            for (MoveErpListVO moveErpListVO : list) {
                row2++;
                String lineId = moveErpListVO.getLineId();
                BaseWmsWarehouseLocator baseWmsWarehouseLocator = data.get(lineId);
                if (ObjectUtils.isNotNull(baseWmsWarehouseLocator)) {
                    String areaCategory = baseWmsWarehouseLocator.getAreaCategory();
                    String warehouseLocatorId = baseWmsWarehouseLocator.getWarehouseLocatorId();
                    String warehouseLocatorCode = baseWmsWarehouseLocator.getWarehouseLocatorCode();
                    String warehouseLocatorName = baseWmsWarehouseLocator.getWarehouseLocatorName();
                    moveErpListVO.setInAreaCategory(areaCategory);
                    //					moveErpListVO.setDestWarehouseCategoryName(areaCategory);
                    moveErpListVO.setInLocatorId(warehouseLocatorId);
                    moveErpListVO.setInLocatorCode(warehouseLocatorId);
                    moveErpListVO.setInLocatorName(warehouseLocatorName);
                } else {
                    log.info(String.format("第[%d]行数据,来源id(移库lineId)[%s]的推荐货位为空!", row2, lineId));
                }
            }
        } else {
            // 打印日志,抛出错误
            log.info("-------远程找寻货位失败!");
            Map<String, BaseWmsWarehouseLocator> data2 = recommendLocatoror.getData();
            String string = "";
            if (data2 != null) {
                string = recommendLocatoror.getMessage() + data2.toString();
            }
            log.info(String.format("远程调用库位推荐失败!错误原因->%s", string));
        }
    }

    private void buildGetLocatorCondition(InstockItemInfo info, MoveErpListVO moveErpListVO, String warehouseId, String cargoOwnerId, String enterpriseId, RestMessage<List<ItemBaseInfoVO>> itemResult) {
        String itemId = moveErpListVO.getItemId();
        String rcvLineId = moveErpListVO.getLineId();
        String rcvHeaderId = moveErpListVO.getBillId();
        BigDecimal pieceQty = moveErpListVO.getOddPieceQuantity();
        BigDecimal conversionRate = moveErpListVO.getCaseConvertionRate();
        String vendorBatchNum = moveErpListVO.getVendorBatchNum();
        String manufacturerLotNum = moveErpListVO.getManufacturerLotNum();
        Date batchExpiredDate = moveErpListVO.getBatchExpiredDate();
        Date batchProductionDate = moveErpListVO.getBatchProductionDate();
        String productionPlace = moveErpListVO.getProductionPlace();
        String whAreaCategoryCode = moveErpListVO.getInAreaCategory();
        info.setRcvId(rcvHeaderId);
        info.setRcvLineId(rcvLineId);
        info.setWarehouseCategoryCode(whAreaCategoryCode);
        info.setEnterpriseId(enterpriseId);
        info.setBatchExpiredDate(batchExpiredDate);
        info.setBatchProductionDate(batchProductionDate);
        info.setCargoOwnerId(cargoOwnerId);
        info.setErpBatchNum(""); // 预留
        info.setItemId(itemId);
        info.setManufacturerLotNum(manufacturerLotNum);
        info.setOrgnId(""); // 预留
        info.setProductionPlace(productionPlace);
        info.setShelfLifeDays(90L);
        info.setPieceCostTe(BigDecimal.ZERO);
        info.setPieceCostTi(BigDecimal.ZERO);
        info.setTaxRate(BigDecimal.ZERO);
        info.setVendorBatchNum(vendorBatchNum);
        info.setVendorId("");
        info.setWarehouseId(warehouseId);
        info.setGoodsCaseCode("");
        info.setItemPieceQty(pieceQty);
        info.setBldgCode("");

        if (itemResult.isSuccess() && CollectionUtil.isNotEmpty(itemResult.getData())) {
            Optional<ItemBaseInfoVO> optional = itemResult.getData().stream().filter(y -> y.getItemId().equals(moveErpListVO.getItemId())).findFirst();
            optional.ifPresent(item -> {
                String storeTempture = item.getStoreTemperature();
                if (StringUtils.isBlank(storeTempture)) {
                    storeTempture = "常温";
                }
                if (!StringUtils.isBlank(storeTempture) && !storeTempture.contains("常温") && !storeTempture.contains("阴凉") && !storeTempture.contains("冷藏") && !storeTempture.contains("冷冻")) {
                    storeTempture = "常温";
                }
                info.setStoreTemperature(storeTempture);
                info.setItemCategoryCode(item.getItemHealthCategory());
            });
        }

        info.setConversionRate(conversionRate);
        info.setContainerCode("");
        info.setBillId(moveErpListVO.getBillId());
        info.setAsnId(moveErpListVO.getBillId());
        info.setLineBillId(moveErpListVO.getLineId());
        String asnHeaderId = moveErpListVO.getBillId();
        String taskInboundId = moveErpListVO.getLineId();
        String batchId = moveErpListVO.getBatchId();
        String inventoryStatus = moveErpListVO.getInStockStatus();
        // 库存状态
        info.setStockStatus(inventoryStatus);
        // 需要加入二级库存状态
        //		String secondStatus = moveErpListVO.getSecondStatus();
        //		if (!StringUtils.isBlank(secondStatus)) {
        //			info.setStockStatus(secondStatus);
        //		}
        info.setAsnId(asnHeaderId);
        info.setRcvLineId(rcvLineId);
        info.setRcvId(rcvHeaderId);
        info.setTaskId(taskInboundId);
        info.setBatchId(batchId);
    }

    @Override
    public Page<MoveErpHeadListVO> getErpMoveHeadList(String warehouseId, OmsMoveBillType billType) {
        UserDTO user = userService.getUser();
        return PageUtils.getPage(adjustmentRecordMapper.getOmsMoveHeadList(user.getEnterpriseId(), warehouseId, billType.name()));
    }

    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public Object createMoveBill(List<MoveBillCreateVO> billCreateVOList, WmsMoveBillType billType) {
        AssertUtils.isNotEmpty(billCreateVOList, "操作的集合不可为空!");
        Map<String, List<MoveBillCreateVO>> billSaveList = billCreateVOList.stream().collect(Collectors.groupingBy(x -> x.getWarehouseId() + "," + x.getCargoOwnerId() + "," + x.getBillId()));
        boolean anyMatch = billCreateVOList.stream().anyMatch(p -> null == p.getQuantity());
        String cargoOwnerId2 = billCreateVOList.get(0).getCargoOwnerId();
        if (WmsMoveBillType.ERP.equals(billType) || WmsMoveBillType.CHECK.equals(billType)) {
            if (!anyMatch) {
                vaildateWholeBill(cargoOwnerId2, billSaveList);
            }
        }

        UserDTO user = userService.getUser();

        String[] locatorIds = Stream.concat(billCreateVOList.stream().map(MoveBillCreateVO::getInLocatorId), billCreateVOList.stream().map(MoveBillCreateVO::getOutLocatorId)).distinct().toArray(String[]::new);
        RestMessage<List<WarehouseLocatorBasicInfo>> locatorResult = warehouseClient.getLocationBasicInfos(locatorIds);
        if (!locatorResult.isSuccess() || CollectionUtil.isEmpty(locatorResult.getData())) {
            throw new BizException("获取库位信息失败");
        }

        String[] whIds = billCreateVOList.stream().map(MoveBillCreateVO::getWarehouseId).distinct().toArray(String[]::new);
        RestMessage<List<BaseWmsWarehouseMst>> whResult = warehouseClient.getWarehouseBasicInfo(whIds);
        if (!whResult.isSuccess() || CollectionUtil.isEmpty(whResult.getData())) {
            throw new BizException("获取仓库信息失败");
        }

        String[] batchIds = billCreateVOList.stream().map(MoveBillCreateVO::getBatchId).distinct().toArray(String[]::new);
        RestMessage<List<BatchNumberListVO>> batchResult = stockResource.getBatchNumberBasicInfo(batchIds);
        if (!batchResult.isSuccess() || CollectionUtil.isEmpty(batchResult.getData())) {
            throw new BizException("获取批次信息失败");
        }

        // 构造移库预占信息
        List<InvStockMove> stockMoves = new ArrayList<>();
        PrintData printMap = new PrintData();
        printMap.setCount(1);
        printMap.setPreview(false);
        printMap.setPrintSolution("YK_ZD.grf");
        printMap.setUrl("http://116.54.19.104:26819/files/YK_ZD.grf");
        List<ReportData> data = new ArrayList<>();
        printMap.setData(data);
        for (Map.Entry<String, List<MoveBillCreateVO>> billEntry : billSaveList.entrySet()) {
            if (vaildateMoveContainer) {
                // 校验容器
                vaildateContainer(billEntry.getValue());
            }

            String warehouseId = billEntry.getKey().split(",")[0];
            String cargoOwnerId = billEntry.getKey().split(",")[1];
            String billId = billEntry.getKey().split(",")[2];

            BaseWmsWarehouseMst warehouseMst = whResult.getData().stream().filter(x -> x.getWarehouseId().equals(warehouseId)).findFirst().orElse(null);
            if (warehouseMst == null) {
                throw new BizException("未找到有效的仓库信息");
            }

            // 查询移库规则
            RestMessage<List<MoveRuleDTO>> moveRuleResult = strategyClient.getMoveList(warehouseMst.getWarehouseId(), null, null);
            if (!moveRuleResult.isSuccess() || CollectionUtil.isEmpty(moveRuleResult.getData())) {
                throw new BizException("获取移库规则失败");
            }

            WmsMoveH moveH = new WmsMoveH();
            moveH.setWarehouseId(warehouseId);
            moveH.setCargoOwnerId(cargoOwnerId);
            moveH.setDcId(warehouseMst.getDcId());
            String next = SequenceApi.getInstance().next("wmsMove");
            moveH.setMoveHeaderId(next);
            moveH.setAutoBillNum(next);
            moveH.setCustomBillNum(moveH.getAutoBillNum());
            moveH.setMoveBillType(billType.name());
            moveH.setSubBillType(WmsMoveSubBillType.MOVE_AREA.getType());
            moveH.setStatus(WmsMoveStatus.NEW.getStatus());
            moveH.setMoveDate(DateUtil.date());
            moveH.setAccessLevel(WmsAccessLevel.ALL.getCode());
            moveH.setPrintCount(0L);
            ModelUtils.setBaseInfo(moveH, ModelUtils.OpType.INSERT, user);
            moveHMapper.save(moveH);

            long i = 1;
            List<WmsMoveD> dList = new ArrayList<>();
            String inbldgName = "";
            String outbldgName = "";
            String warehouseWorkerId = "";
            List<Map<String, Object>> table = new ArrayList<>();
            for (MoveBillCreateVO saveVO : billEntry.getValue()) {

                WarehouseLocatorBasicInfo inLocator = locatorResult.getData().stream().filter(x -> x.getWarehouseLocatorId().equals(saveVO.getInLocatorId())).findFirst().orElse(null);
                if (inLocator == null) {
                    throw new BizException("未找到有效的上架货位信息");
                }
                inbldgName = inLocator.getBldgName();

                WarehouseLocatorBasicInfo outLocator = locatorResult.getData().stream().filter(x -> x.getWarehouseLocatorId().equals(saveVO.getOutLocatorId())).findFirst().orElse(null);
                if (outLocator == null) {
                    throw new BizException("未找到有效的下架货位信息");
                }
                outbldgName = outLocator.getBldgName();

                BatchNumberListVO batchNumber = batchResult.getData().stream().filter(x -> x.getBatchId().equals(saveVO.getBatchId())).findFirst().orElse(null);
                if (batchNumber == null) {
                    throw new BizException("未找到有效的批次信息");
                }

                String status = WmsMoveDStatus.ERP_AUDIT_PASS.getStatus();
                if (!inLocator.getWarehouseAreaCategory().equals(outLocator.getWarehouseAreaCategory())) {
                    MoveRuleDTO moveRule = moveRuleResult.getData().stream().filter(x -> x.getSrcWarehouseCategory().equals(outLocator.getWarehouseAreaCategory()) && x.getDestWarehouseCategory().equals(inLocator.getWarehouseAreaCategory())).findFirst().orElse(null);
                    if (moveRule == null) {
                        throw new BizException("未找到有效的移库规则");
                    }

                    if (moveRule.getAuditFlag() == 1) {
                        status = WmsMoveDStatus.NEW.getStatus();
                    }
                }
                if (saveVO.getQuantity() == null) {
                    saveVO.setCaseConvertionRate(batchNumber.getCaseConversionRate());
                    saveVO.setQuantity(saveVO.getCaseQuantity().multiply(saveVO.getCaseConvertionRate()).add(saveVO.getOddPieceQuantity()));
                }

                // 校验是否超过原移库明细总量
                vaildateOvercharge(saveVO);

                WmsMoveD moveD = new WmsMoveD();
                moveD.setMoveHeaderId(moveH.getMoveHeaderId());
                moveD.setOutLocatorId(saveVO.getOutLocatorId());
                moveD.setInLocatorId(saveVO.getInLocatorId());
                moveD.setItemId(batchNumber.getItemId());
                moveD.setUom(batchNumber.getCaseUom());
                moveD.setBatchId(batchNumber.getBatchId());
                moveD.setBatchNum(batchNumber.getBatchNum());
                moveD.setConversionRate(batchNumber.getPieceConversionRate());
                moveD.setCaseConversionRate(batchNumber.getCaseConversionRate());
                moveD.setQuantity(saveVO.getCaseQuantity());
                moveD.setOddPieceQuantity(saveVO.getOddPieceQuantity());
                moveD.setPieceQuantity(moveD.getQuantity().multiply(moveD.getCaseConversionRate()).add(moveD.getOddPieceQuantity()));
                moveD.setComments(saveVO.getComments());
                warehouseWorkerId = saveVO.getWarehouseWorkerId();
                moveD.setWarehouseWorkerId(saveVO.getWarehouseWorkerId());
                moveD.setReason(saveVO.getReason());
                moveD.setLineNum(i++);
                moveD.setStatus(status);
                moveD.setInStockStatus(saveVO.getInStockStatus());
                moveD.setOutStockStatus(saveVO.getOutStockStatus());
                moveD.setErpPlanQuantity(saveVO.getErpPlanQuantity());
                moveD.setErpReason(saveVO.getErpReason());
                moveD.setManufacturerLotNum(batchNumber.getManufacturerLotNum());
                moveD.setBatchProductionDate(batchNumber.getBatchProductionDate());
                moveD.setBatchExpiredDate(batchNumber.getBatchExpiredDate());
                moveD.setBatchProductionDateShow(batchNumber.getBatchProductionDateShow());
                moveD.setBatchExpiredDateShow(batchNumber.getBatchExpiredDateShow());
                moveD.setMoveOutBldg(outLocator.getBldgId());
                moveD.setMoveInBldg(inLocator.getBldgId());
                moveD.setBillId(saveVO.getBillId());
                moveD.setBillNo(saveVO.getBillNo());
                moveD.setLineId(saveVO.getLineId());
                moveD.setOutContainerCode(saveVO.getOutContainerCode());
                moveD.setOutGoodsCaseCode(saveVO.getOutGoodsCaseCode());
                if ("JCK".equals(inLocator.getWarehouseAreaCategory())) {// 正方体临时解决方案，借出库先走货，后补出库流程功能,通过空出实现
                    moveD.setInStockSubStatus("1");
                }
                ModelUtils.setBaseInfo(moveD, ModelUtils.OpType.INSERT, user);
                dList.add(moveD);

                MovePrintDetailVO pd = new MovePrintDetailVO();
                BeanUtils.copyProperties(moveD, pd);
                pd.setItemCode(batchNumber.getItemCode());
                pd.setItemName(batchNumber.getItemName());
                pd.setUom(batchNumber.getCaseUom());
                pd.setManufacturerId(batchNumber.getManufacturerId());
                pd.setManufacturerName(batchNumber.getManufacturerId());
                pd.setCaseConversionRate(batchNumber.getCaseConversionRate());
                pd.setSpecification(batchNumber.getSpecification());
                pd.setInWarehouseAreaCategory(inLocator.getWarehouseAreaCategory());
                pd.setInLocatorName(inLocator.getWarehouseLocatorName());
                pd.setInLocatorCode(inLocator.getWarehouseLocatorCode());
                pd.setOutWarehouseAreaCategory(outLocator.getWarehouseAreaCategory());
                pd.setOutLocatorName(outLocator.getWarehouseLocatorName());
                pd.setOutLocatorCode(outLocator.getWarehouseLocatorCode());
                pd.setWarehouseWorkerName(user.getNickname());
                Map<String, Object> tableMap = BeanConvertUtils.convertBean2MapForIngoreserialVersionUID(pd);
                table.add(tableMap);
            }
            moveDMapper.saveBatch(dList);
            // 构造移库预占信息
            stockMoves.addAll(dList.stream().map(x -> getInvStockMove(x, moveH, locatorResult.getData())).collect(Collectors.toList()));
            // 构造打印数据
            // TODO
            MovePrintHeadVO ph = new MovePrintHeadVO();
            ph.setDeptName("");
            ph.setEnterpriseName(user.getEnterpriseName());
            ph.setMoveHeaderId(next);
            ph.setMoveInBldgName(inbldgName);
            ph.setMoveOutBldgName(outbldgName);
            ph.setPrintUser(user.getUsername());
            ph.setWorkerNo(warehouseWorkerId);
            Map<String, Object> head = BeanConvertUtils.convertBean2MapForIngoreserialVersionUID(ph);
            ReportData dataMap = new ReportData();
            dataMap.setHead(head);
            dataMap.setTable(table);
            data.add(dataMap);
        }
        if (WmsMoveBillType.ERP.equals(billType) || WmsMoveBillType.CHECK.equals(billType)) {
            if (anyMatch) {
                vaildateWholeBill(cargoOwnerId2, billSaveList);
            }
        }

        if (CollectionUtil.isEmpty(stockMoves)) {
            throw new BizException("移库失败! ");
        }

        // 货主转换不执行库存预占, 前面环节已处理了库存
        if (!billType.equals(WmsMoveBillType.SC) && !billType.equals(WmsMoveBillType.GSP)) {
            // 预占
            RestMessage restMessage = stockResource.moveStock(stockMoves);
            if (!restMessage.isSuccess()) {
                throw new BizException("调用远程库存服务预占库存" + restMessage.getMessage());
            }
        }

        if (billType.equals(WmsMoveBillType.GSP)) {
            // 确认移库GSP信息
            Set<String> maintainIds = billCreateVOList.stream().map(MoveBillCreateVO::getLineId).collect(Collectors.toSet());
            // 取消养护预占
            InvStockCancel cancelMaintain = new InvStockCancel();
            cancelMaintain.setLineBillIds(maintainIds.toArray(new String[]{}));
            RestMessage maintainMessage = stockResource.maintainCancel(cancelMaintain);
            if (!maintainMessage.isSuccess()) {
                throw new BizException(maintainMessage.getMessage());
            }
            //移库预占
            RestMessage restMessage = stockResource.moveStock(stockMoves);
            if (!restMessage.isSuccess()) {
                throw new BizException("调用远程库存服务预占库存" + restMessage.getMessage());
            }
            // 通知GSP移库完成
            RestMessage gspResult = gspClient.confirmUpkeepMove(maintainIds.toArray(new String[]{}));
            if (!gspResult.isSuccess()) {
                throw new BizException(gspResult.getMessage());
            }
        }
        return printMap;
    }

    private void vaildateOvercharge(MoveBillCreateVO saveVO) {
        String lineId = saveVO.getLineId();
        String billId = saveVO.getBillId();
        BigDecimal quantity = saveVO.getQuantity();
        if (quantity == null || BigDecimal.ZERO.compareTo(quantity) == 0) {
            throw new BizException("移动的数量不可为0!");
        }
        List<OmsStockAdjustmentRecord> findByMap = this.adjustmentRecordMapper.findByMap(MapUtils.put("lineid", lineId).put("billid", billId).getMap(), OmsStockAdjustmentRecord.class);
        if (!ObjectUtils.isEmpty(findByMap)) {
            BigDecimal reduce = findByMap.stream().map(p -> p.getQuantity() == null ? BigDecimal.ZERO : p.getQuantity()).reduce(BigDecimal.ZERO, BigDecimal::add);
            if (quantity.compareTo(reduce) == 1) {
                throw new BizException("移动数量不可大于原数量!");
            }
        }
    }

    /**
     * 校验是否为整单提取
     *
     * @param cargoOwnerId
     * @param map
     */
    private void vaildateWholeBill(String cargoOwnerId, Map<String, List<MoveBillCreateVO>> map) {

        RestMessage<Map<String, BaseCargoOwnerMst>> restMessage = chainResource.getCargoOwerByIds(new HashSet<>(Arrays.asList(cargoOwnerId)));
        if (!restMessage.isSuccess()) {
            return;
        }

        Map<String, BaseCargoOwnerMst> mstMap = restMessage.getData();
        if (com.prolog.framework.utils.ObjectUtils.isNotNull(mstMap)) {
            BaseCargoOwnerMst ownerMst = mstMap.values().stream().collect(Collectors.toList()).get(0);
            if ("1".equals(String.valueOf(ownerMst.getIsMoveMtlReturn()))) {
                return;
            }
        }
        for (Map.Entry<String, List<MoveBillCreateVO>> billEntry : map.entrySet()) {
            List<MoveBillCreateVO> value = billEntry.getValue();
            MoveBillCreateVO moveBillCreateVO = value.get(0);
            String billId = moveBillCreateVO.getBillId();
            Criteria crt = Criteria.forClass(OmsStockAdjustmentRecord.class);
            crt.setRestriction(Restrictions.eq("billid", billId));
            List<OmsStockAdjustmentRecord> record = adjustmentRecordMapper.findByCriteria(crt);
            if (ObjectUtils.isEmpty(record)) {
                // 应当抛错的
                continue;
            }
            BigDecimal reduce = record.stream().map(OmsStockAdjustmentRecord::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal reduce2 = value.stream().map(MoveBillCreateVO::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
            if (reduce.compareTo(reduce2) != 0) {
                // 不相等,说明不是整单操作
                throw new BizException("当前货主需要整单操作移库数据!请检查!");
            }
        }
    }

    @Override
    public Page<PdaMoveListVO> getMoveDetails(String headerId, String code, String containercode, String status) {
        UserDTO user = userService.getUser();
        if (StringUtils.isBlank(headerId) && StringUtils.isBlank(containercode)) {
            throw new BizException("请输入单据编号或者容器号！");
        }

        List<ItemBaseInfoVO> baseInfoVOList = null;
        if (StrUtil.isNotBlank(code)) {
            // 请求商品信息
            RestMessage<List<ItemBaseInfoVO>> itemResult = itemResource.getItemBaseInfoByCode(code, null);
            if (!itemResult.isSuccess() || itemResult.getData() == null) {
                throw new BizException("未找到有效的商品信息");
            }

            baseInfoVOList = itemResult.getData();
        }

        WmsMoveDStatus moveStatus = WmsMoveDStatus.get(status);
        if (moveStatus == null) {
            throw new BizException("未知的移库状态");
        }

        Criteria criteria = Criteria.forClass(WmsMoveD.class);
        criteria.restrictionBuilder().eq("moveHeaderId", headerId).or().eq("outContainerCode", containercode).eq("status", moveStatus.getStatus()).eq("enterpriseId", user.getEnterpriseId());
        if (baseInfoVOList != null) {
            criteria.restrictionBuilder().in("itemId", baseInfoVOList.stream().map(ItemBaseInfoVO::getItemId).toArray(String[]::new));
        }
        criteria.buildRestriction();
        Page<WmsMoveD> dList = PageUtils.getPage(moveDMapper.findByCriteria(criteria));

        if (CollectionUtil.isEmpty(dList.getList())) {

            return null;
        }
        headerId = dList.getList().get(0).getMoveHeaderId();
        // 查找移库单信息
        Criteria criteriaH = Criteria.forClass(WmsMoveH.class);
        criteriaH.restrictionBuilder().eq("moveHeaderId", headerId).eq("subBillType", WmsMoveSubBillType.MOVE_AREA.getType()).eq("enterpriseId", user.getEnterpriseId());
        criteriaH.buildRestriction();
        List<WmsMoveH> hList = moveHMapper.findByCriteria(criteriaH);
        if (hList == null || hList.isEmpty()) {
            throw new BizException("未找到有效的移库单");
        }

        // 组合id
        String[] itemIds = dList.getList().stream().map(WmsMoveD::getItemId).distinct().toArray(String[]::new);

        // 请求商品信息
        RestMessage<List<ItemBaseInfoVO>> itemResult = itemResource.getItemBaseList(itemIds);

        // 组合id
        String[] locatorIds = Stream.concat(dList.getList().stream().map(WmsMoveD::getOutLocatorId), dList.getList().stream().map(WmsMoveD::getInLocatorId)).distinct().toArray(String[]::new);

        // 组合id
        RestMessage<Map<String, BaseCargoOwnerMst>> cargoResult = chainResource.getCargoOwerByIds(Collections.singleton(hList.get(0).getCargoOwnerId()));

        // 请求货位信息
        RestMessage<List<WarehouseLocatorBasicInfo>> locatorResult = warehouseClient.getLocationBasicInfos(locatorIds);

        // 组合id
        String[] batchIds = dList.getList().stream().map(WmsMoveD::getBatchId).distinct().toArray(String[]::new);
        // 请求库存批次信息
        RestMessage<List<BatchNumberListVO>> stockResult = stockResource.getBatchNumberBasicInfo(batchIds);

        return BeanConvertUtils.convertPageTo(dList, PdaMoveListVO::new, (s, t) -> {

            if (cargoResult.isSuccess() && cargoResult.getData() != null) {
                BaseCargoOwnerMst cargoOwnerMst = cargoResult.getData().get(hList.get(0).getCargoOwnerId());
                t.setCargoOwnerId(cargoOwnerMst.getCargoOwnerId());
                t.setCargoOwnerName(cargoOwnerMst.getCargoOwnerName());
            }

            if (itemResult.isSuccess() && itemResult.getData() != null) {
                Optional<ItemBaseInfoVO> optional = itemResult.getData().stream().filter(y -> y.getItemId().equals(t.getItemId())).findFirst();
                optional.ifPresent(item -> {
                    t.setItemCode(item.getItemCode());
                    t.setItemName(item.getItemName());
                });
            }

            if (locatorResult.isSuccess() && locatorResult.getData() != null) {
                Optional<WarehouseLocatorBasicInfo> optionalIn = locatorResult.getData().stream().filter(y -> y.getWarehouseLocatorId().equals(t.getInLocatorId())).findFirst();
                optionalIn.ifPresent(locator -> {
                    t.setInWarehouseAreaCategory(locator.getWarehouseAreaCategory());
                    t.setInLocatorName(locator.getWarehouseLocatorName());
                    t.setInLocatorCode(locator.getWarehouseLocatorCode());
                });

                Optional<WarehouseLocatorBasicInfo> optionalOut = locatorResult.getData().stream().filter(y -> y.getWarehouseLocatorId().equals(t.getOutLocatorId())).findFirst();
                optionalOut.ifPresent(locator -> {
                    t.setOutWarehouseAreaCategory(locator.getWarehouseAreaCategory());
                    t.setOutLocatorName(locator.getWarehouseLocatorName());
                    t.setOutLocatorCode(locator.getWarehouseLocatorCode());
                });
            }

            if (stockResult.isSuccess() && stockResult.getData() != null) {
                Optional<BatchNumberListVO> optional = stockResult.getData().stream().filter(y -> y.getBatchId().equals(t.getBatchId())).findFirst();
                optional.ifPresent(batchInfo -> {
                    t.setManufacturerLotNum(batchInfo.getManufacturerLotNum());
                });
            }
        });
    }

    @Override
    public PdaMoveDetailVO getMoveDetail(String moveLineId) {
        UserDTO user = userService.getUser();
        Criteria criteria = Criteria.forClass(WmsMoveD.class);
        criteria.restrictionBuilder().eq("moveLineId", moveLineId).eq("enterpriseId", user.getEnterpriseId());
        criteria.buildRestriction();
        List<WmsMoveD> dList = moveDMapper.findByCriteria(criteria);
        if (dList == null || dList.isEmpty()) {
            throw new BizException("未找到有效的移库单详情");
        }

        WmsMoveD info = dList.get(0);

        WmsMoveH moveH = moveHMapper.findById(info.getMoveHeaderId(), WmsMoveH.class);
        if (moveH == null) {
            throw new BizException("未找到有效的移库单");
        }

        // 请求货位信息
        RestMessage<List<WarehouseLocatorBasicInfo>> locatorResult = warehouseClient.getLocationBasicInfos(new String[]{info.getInLocatorId(), info.getOutLocatorId()});

        // 组合id
        RestMessage<Map<String, BaseCargoOwnerMst>> cargoResult = chainResource.getCargoOwerByIds(Collections.singleton(moveH.getCargoOwnerId()));

        RestMessage<List<ItemBaseInfoVO>> itemResult = itemResource.getItemBaseList(new String[]{info.getItemId()});

        WmsInvStockQtyQueryVO queryVO = new WmsInvStockQtyQueryVO();
        queryVO.setEnterpriseId(user.getEnterpriseId());
        queryVO.setCargoOwnerId(moveH.getCargoOwnerId());
        queryVO.setWarehouseId(moveH.getWarehouseId());
        queryVO.setLocatorId(info.getOutLocatorId());
        queryVO.setBatchId(info.getBatchId());
        queryVO.setItemId(info.getItemId());
        queryVO.setStatus(info.getOutStockStatus());

        // 请求库存批次信息
        RestMessage<List<WmsInvStockQtyVO>> stockResult = stockResource.getAvailableQuantity(BeanUtil.beanToMap(queryVO));
        return BeanConvertUtils.convertTo(info, PdaMoveDetailVO::new, (s, t) -> {
            t.setOddQuantity(s.getOddPieceQuantity());
            t.setPieceQuantity(s.getPieceQuantity());
            t.setCaseQuantity(s.getQuantity());
            if (locatorResult.isSuccess() && locatorResult.getData() != null && !locatorResult.getData().isEmpty()) {
                WarehouseLocatorBasicInfo inLocator = locatorResult.getData().stream().filter(x -> x.getWarehouseLocatorId().equals(info.getInLocatorId())).findFirst().orElse(null);
                if (inLocator != null) {
                    t.setInLocatorCode(inLocator.getWarehouseLocatorCode());
                    t.setInLocatorName(inLocator.getWarehouseLocatorName());
                    t.setInWarehouseAreaCategory(inLocator.getWarehouseAreaCategory());
                }

                WarehouseLocatorBasicInfo outLocator = locatorResult.getData().stream().filter(x -> x.getWarehouseLocatorId().equals(info.getOutLocatorId())).findFirst().orElse(null);
                if (outLocator != null) {
                    t.setOutLocatorCode(outLocator.getWarehouseLocatorCode());
                    t.setOutLocatorName(outLocator.getWarehouseLocatorName());
                    t.setOutWarehouseAreaCategory(outLocator.getWarehouseAreaCategory());
                }
            }
            if (cargoResult.isSuccess() && cargoResult.getData() != null && !cargoResult.getData().isEmpty()) {
                BaseCargoOwnerMst cargo = cargoResult.getData().get(moveH.getCargoOwnerId());
                t.setCargoOwnerName(cargo.getCargoOwnerName());
                t.setCargoOwnerId(cargo.getCargoOwnerId());
            }

            if (itemResult.isSuccess() && itemResult.getData() != null && !itemResult.getData().isEmpty()) {
                ItemBaseInfoVO item = itemResult.getData().get(0);
                t.setItemCode(item.getItemCode());
                t.setItemName(item.getItemName());
                t.setCaseConversionRate(item.getCaseConversionRate());
                t.setCaseUom(item.getCaseUom());
                t.setSpecification(item.getSpecification());
                t.setManufacturerId(item.getManufacturerId());
                t.setManufacturerName(item.getManufacturerId());
                t.setCountryOriginId(item.getCountryOriginId());
                t.setCountryOriginName(item.getCountryOriginId());
            }

            if (stockResult.isSuccess() && stockResult.getData() != null && !stockResult.getData().isEmpty()) {
                WmsInvStockQtyVO batchInfo = stockResult.getData().get(0);

                t.setBatchNum(batchInfo.getBatchNum());
                t.setBatchProductionDate(batchInfo.getBatchProductionDate());
                t.setBatchExpiredDate(batchInfo.getBatchExpiredDate());
                t.setAvailableQuantity(batchInfo.getAvailableQuantity());
                t.setManufacturerLotNum(batchInfo.getManufacturerLotNum());
            }
        });
    }

    @Override
    @TxTransaction(isStart = true)
    @Transactional(rollbackFor = Exception.class)
    public String move(String moveLineId, Integer inOrOut) {
        String msg = "操作成功！";
        UserDTO user = userService.getUser();

        Criteria criteria = Criteria.forClass(WmsMoveD.class);
        criteria.restrictionBuilder().eq("moveLineId", moveLineId).eq("enterpriseId", user.getEnterpriseId());
        criteria.buildRestriction();
        List<WmsMoveD> dList = moveDMapper.findByCriteria(criteria);
        Assert.notEmpty(dList, "未找到有效的移库明细");

        WmsMoveD dInfo = dList.get(0);

        // 审核后才能移出/入
        if (inOrOut == 0 && !dInfo.getStatus().equals(WmsMoveDStatus.ERP_AUDIT_PASS.getStatus())) {
            throw new BizException("请先审核移库单再操作下架");
        } else if (inOrOut == 1 && !dInfo.getStatus().equals(WmsMoveDStatus.WMS_MOVE_OUT.getStatus())) {
            throw new BizException("请先将商品下架后再上架");
        }

        WmsMoveDStatus dStatus = inOrOut == 0 ? WmsMoveDStatus.WMS_MOVE_OUT : WmsMoveDStatus.WMS_MOVE_IN;
        dInfo.setStatus(dStatus.getStatus());
        ModelUtils.setBaseInfo(dInfo, ModelUtils.OpType.UPDATE, user);
        if (moveDMapper.update(dInfo) == 0) {
            throw new BizException("移库失败,请稍后再试");
        }

        Set<String> syncIds = new HashSet<>();
        if (inOrOut == 1) {
            // 查询所有需要同步的主表信息
            String[] statusList = new String[]{WmsMoveDStatus.NEW.getStatus(), WmsMoveDStatus.WMS_AUDIT_PASS.getStatus(), WmsMoveDStatus.WMS_SYNC.getStatus(), WmsMoveDStatus.ERP_AUDIT_PASS.getStatus(), WmsMoveDStatus.WMS_MOVE_OUT.getStatus()};
            // 查询WMS发起的操作完毕的移库单
            List<MoveSyncInfoVO> syncInfoList = moveHMapper.getMoveDoneByLineBill(user.getEnterpriseId(), WmsMoveBillType.WMS.name(), WmsMoveSubBillType.MOVE_AREA.getType(), new String[]{moveLineId}, statusList);
            if (CollectionUtil.isNotEmpty(syncInfoList)) {
                syncIds = genOmsStockAdjustResult(syncInfoList, user);
            }

            // 查询货主转换单发起的操作完毕的移库单
            List<MoveSyncInfoVO> cargoChangeList = moveHMapper.getMoveDoneByLineBill(user.getEnterpriseId(), WmsMoveBillType.SC.name(), WmsMoveSubBillType.MOVE_AREA.getType(), new String[]{moveLineId}, statusList);
            Set<String> cargoChangeHeaderIds = cargoChangeList.stream().map(MoveSyncInfoVO::getBillId).collect(Collectors.toSet());
            // 判断是否存在货主转换单移库
            if (CollectionUtil.isNotEmpty(cargoChangeHeaderIds)) {
                cargoChangeService.changeCargoComplete(cargoChangeHeaderIds);
            }
        }

        InvStockCheckMove checkMove = new InvStockCheckMove();
        if (dStatus == WmsMoveDStatus.WMS_MOVE_IN) {
            checkMove.setMoveFlag(1);
        } else {
            checkMove.setMoveFlag(0);
        }

        // 查询货主转换单发起的操作完毕的移库单
        List<MoveSyncInfoVO> cargoChangeList = moveHMapper.getMoveDoneByLineBill(user.getEnterpriseId(), WmsMoveBillType.SC.name(), WmsMoveSubBillType.MOVE_AREA.getType(), new String[]{moveLineId}, null);
        Collection<String> detailIds = cargoChangeList.stream().map(MoveSyncInfoVO::getMoveLineId).collect(Collectors.toSet());
        Collection<String> lineIds = Collections.singletonList(moveLineId);
        if (CollectionUtil.isNotEmpty(detailIds)) {
            lineIds = CollectionUtil.subtract(lineIds, detailIds);
        }
        checkMove.setLineBillIds(lineIds.toArray(new String[0]));

        if (CollectionUtil.isNotEmpty(lineIds)) {
            // 请求库存服务处理库存
            RestMessage restMessage = stockResource.moveStockCheck(checkMove);
            if (!restMessage.isSuccess()) {
                throw new BizException(restMessage.getMessage());
            }
        }
        senSyncMessage(syncIds);
        /**
         * 若下架记录行中是否存在【移出库别】=【立库】的下架记录行，校验下架商品对应的托盘上待下架的商品是否全部下架，若全部已下架，
         * 则下架确认后则调用接口【作业完成确认接口】（作业类型=30 移库出库）发送EIS盘点操作结束指令（托盘回库，释放站台），接口中只记录实际移库下架的商品。
         */
        if (dStatus.equals(WmsMoveDStatus.WMS_MOVE_OUT)) {
            // 获取库位信息
            String[] locatorIds = dList.stream().map(WmsMoveD::getOutLocatorId).distinct().toArray(String[]::new);
            RestMessage<List<WarehouseLocatorBasicInfo>> locatorResult = warehouseClient.getLocationBasicInfos(locatorIds);
            if (!locatorResult.isSuccess() || CollectionUtil.isEmpty(locatorResult.getData())) {
                throw new BizException("获取库位信息失败");
            }
            List<String> declocatorIds = locatorResult.getData().stream().filter(loc -> loc.getPickMode().equals(PICK_MODE_DEVICE)).map(WarehouseLocatorBasicInfo::getWarehouseLocatorId).collect(Collectors.toList());
            //过滤立库的明细
            List<String> deviceWmsMOveContainer = dList.stream().filter(moved -> declocatorIds.contains(moved.getOutLocatorId())).map(WmsMoveD::getOutContainerCode).distinct().collect(Collectors.toList());
            //校验下架商品对应的托盘上是否存在有其他商品未下架
            //判断托盘是否还有库存
            ByContainerVo byContainerVo = new ByContainerVo();
            byContainerVo.setContainerCodes(deviceWmsMOveContainer);
            RestMessage<Map<String, List<WmsInvStockQtyVO>>> containerStockMsg = stockResource.listStockLotByContainerCode(byContainerVo);
            for (String outcontainercode : deviceWmsMOveContainer) {
                int unconfirmnum = moveDMapper.getUncomfirmContainerNum(WmsMoveDStatus.ERP_AUDIT_PASS.getStatus(), new String[]{moveLineId}, outcontainercode);
                if (unconfirmnum == 0) {
                    //给eis下发回库指令
                    ContainerConfirmDto vo = new ContainerConfirmDto();
                    String containerType = "3";
                    boolean isAllZero = true;
                    String warehouseCategory = "";
                    if (containerStockMsg.isSuccess() && CollUtil.isNotEmpty(containerStockMsg.getData())) {
                        List<WmsInvStockQtyVO> lotList = containerStockMsg.getData().get(outcontainercode);
                        for (WmsInvStockQtyVO stockQtyVO : lotList) {
                            if (isAllZero && stockQtyVO.getQuantity().doubleValue() > 0) {
                                isAllZero = false;
                            }
                            if (StrUtil.isEmpty(warehouseCategory)) {
                                warehouseCategory = stockQtyVO.getWarehouseAreaCategory();
                            }
                        }
                    }
                    if (isAllZero) {
                        if (warehouseCategory.equals("MJK")) {
                            containerType = "2";
                        }
                        if (warehouseCategory.equals("AGVZ")) {
                            containerType = "1";
                        }
                    } else {
                        containerType = "3";
                    }
                    vo.setContainerType(containerType);
                    MoveQueryDVO queryVO = new MoveQueryDVO();
                    queryVO.setOutcontainercode(outcontainercode);
                    queryVO.setStatusList(new String[]{WmsMoveDStatus.WMS_MOVE_OUT.getStatus()});
                    List<MoveDListVO> dMoveList = moveDMapper.selectMoveDInfoList(user.getEnterpriseId(), queryVO);
                    List<String> inlocids = dMoveList.stream().map(MoveDListVO::getInLocatorId).collect(Collectors.toList());
                    RestMessage<List<WarehouseLocatorBasicInfo>> inlocatorResult = warehouseClient.getLocationBasicInfos(inlocids.toArray(new String[]{}));
                    if (!inlocatorResult.isSuccess() || CollectionUtil.isEmpty(inlocatorResult.getData())) {
                        throw new BizException("获取库位信息失败");
                    }

                    dMoveList = dMoveList.stream().filter(movevo -> StringUtils.isBlank(movevo.getEisFinishFlag())).collect(Collectors.toList());
                    List<EisConfirmSubVo> finshsubvos = new ArrayList<>();
                    for (MoveDListVO moveDListVO : dMoveList) {
                        BaseMtlItemMst item = RestMsgHelper.unpack(itemResource.item(moveDListVO.getItemId()));
                        moveDListVO.setItemCode(item.getItemCode());
                        WarehouseLocatorBasicInfo outLocator = locatorResult.getData().stream().filter(x -> x.getWarehouseLocatorId().equals(moveDListVO.getOutLocatorId())).findFirst().orElse(null);
                        EisConfirmSubVo subfinishvo = this.genEisConfirmSubVo(moveDListVO, item, outLocator);
                        finshsubvos.add(subfinishvo);
                    }
                    List<String> dlineids = dMoveList.stream().map(MoveDListVO::getMoveLineId).collect(Collectors.toList());
                    moveDMapper.updateEisFinishFlag(dlineids);
                    vo.setSubList(finshsubvos);
                    //FIXME:调接口封装参数
//                    mqProducer.operationComplete(vo);
                    if (!vo.getContainerType().equals("3")) {
                        //释放容器
                        ContainerQueryVO containerQueryVO = new ContainerQueryVO();
                        containerQueryVO.setContainerCode(vo.getContainerNo());
                        containerQueryVO.setEnterpriseId(dList.get(0).getEnterpriseId());
                        containerQueryVO.setWarehouseId(locatorResult.getData().get(0).getWarehouseId());
                        RestMessage<Object> release = warehouseClient.release(containerQueryVO);
                        log.info("托盘库存已经为0，托盘解锁->{},{}", JSONObject.toJSONString(containerQueryVO), JSONObject.toJSONString(release));
                    }
                } else {
                    msg = "容器存在其他商品待下架。";
                }
            }
        }
        return msg;
    }

    private EisConfirmSubVo genEisConfirmSubVo(
            MoveDListVO wmsMoveD,
            BaseMtlItemMst itemMst,
            WarehouseLocatorBasicInfo locator
    ){
        EisConfirmSubVo subVo = new EisConfirmSubVo();
        subVo.setMsgId(cn.hutool.core.util.IdUtil.fastSimpleUUID());
        subVo.setTaskId(wmsMoveD.getBillId());
        subVo.setSeqNo(wmsMoveD.getLineId());
        subVo.setBusinessType("20");
        subVo.setWorkType("20");
        subVo.setItemId(wmsMoveD.getItemId());
        subVo.setLotId(wmsMoveD.getBatchId());
        subVo.setOwnerId(itemMst.getCargoOwnerId());
        String packId = "";
        if (CollUtil.isNotEmpty(itemMst.getBaseMtlItemUomConversionsList())) {
            packId = itemMst.getBaseMtlItemUomConversionsList().get(0).getItemUomId();
        }
        subVo.setPackId(packId);
        subVo.setWarehouseId("JXYJ");
        subVo.setWmslocationCode(locator.getWarehouseLocatorCode());
        return subVo;
    }

    private void senSyncMessage(Set<String> syncIds) {
        // 发送同步信息
        if (CollectionUtil.isNotEmpty(syncIds)) {
            EtlMessage message = new EtlMessage();
            message.setId(String.join(",", syncIds));
            message.setEtlType(EtlTypeConstants.op_mv_audit_w2o);
            try {
                exchangeSender.send(message);
            } catch (JsonProcessingException e) {
                log.error("移库信息同步消息发送失败", e);
                throw new PrologException("移库信息同步消息发送失败");
            }
        }
    }

    private InvStockMove getInvStockMove(WmsMoveD dInfo, WmsMoveH hInfo, List<WarehouseLocatorBasicInfo> locatorResult) {
        InvStockMove stockMove = new InvStockMove();
        stockMove.setBillId(dInfo.getMoveHeaderId());
        stockMove.setWarehouseId(hInfo.getWarehouseId());
        stockMove.setLineBillId(dInfo.getMoveLineId());

        // 移入
        InvStockMoveDetail moveDetail = new InvStockMoveDetail();
        moveDetail.setBatchId(dInfo.getBatchId());
        moveDetail.setInLocatorId(dInfo.getInLocatorId());
        moveDetail.setQuantity(dInfo.getPieceQuantity());
        moveDetail.setInContainerCode(dInfo.getInContainerCode());
        moveDetail.setInGoodsCaseCode(dInfo.getInGoodsCaseCode());
        moveDetail.setComments(dInfo.getComments());
        moveDetail.setInStockStatus(dInfo.getInStockStatus());
        // 移出
        WarehouseLocatorBasicInfo outLocator = locatorResult.stream().filter(x -> x.getWarehouseLocatorId().equals(dInfo.getOutLocatorId())).findFirst().orElse(null);
        if (outLocator == null) {
            throw new BizException("未找到有效的下架货位信息");
        }

        moveDetail.setOutLocatorId(dInfo.getOutLocatorId());
        moveDetail.setQuantity(dInfo.getPieceQuantity());
        if (!outLocator.getWarehouseAreaCategory().equals("AGVL")){
            moveDetail.setOutContainerCode(dInfo.getOutContainerCode());
        }else {
            moveDetail.setOutContainerCode("N/A");
        }
        moveDetail.setOutGoodsCaseCode(dInfo.getOutGoodsCaseCode());
        moveDetail.setComments(dInfo.getComments());
        moveDetail.setOutStockStatus(dInfo.getOutStockStatus());
        if (!StringUtils.isBlank(dInfo.getInStockSubStatus())) {
            moveDetail.setInStockSubStatus(dInfo.getInStockSubStatus());
        }
        stockMove.setDetails(Collections.singletonList(moveDetail));
        return stockMove;
    }

    @Override
    @Transactional
    public Object inReceive(Map<String, Object> map) {
        // 接收移库入任务回传
        IntegrationSearchData sd = new IntegrationSearchData();
        sd.setInterfaceId(instockCallId);
        Map<String, Object> data2 = MapUtils.put("type", "1").getMap();
        sd.setData(data2);
        List<Map> mapList = null;
        try {
            String string = JsonUtils.toString(sd);
            log.info("索取移库上架回传请求参数->" + string);
            mapList = integrationApi.searchForRestMessageMap(sd);
        } catch (Exception e) {
            log.error("索取移库上架回传,调用接口平台失败,原因->", e.getMessage());
            throw new BizException("索取移库上架回传,调用接口平台失败,原因->" + e.getMessage());
        }
        if (ObjectUtils.isEmpty(mapList)) {
            throw new BizException("索取移库上架回传的数据为空!请重试!");
        }
        Set<String> collect = mapList.stream().filter(p -> !StringUtils.isEmpty((String) p.get("TASK_ID"))).map(p -> (String) p.get("TASK_ID")).collect(Collectors.toSet());
        if (!ObjectUtils.isEmpty(collect)) {
            // 执行移库确认逻辑
            MoveConfirmParamsVO vo = new MoveConfirmParamsVO();
            vo.setMoveFlag(3);
            vo.setLineIds(collect.toArray(new String[collect.size()]));
            this.confirmMove(vo);

            // 更新回传状态
            IntegrationSearchData sd2 = new IntegrationSearchData();
            for (String string : collect) {
                sd2.setInterfaceId(instockCallStatusId);
                List<Map<String, Object>> paraListMap = new ArrayList<>();
                Map<String, Object> map2 = MapUtils.put("task_id", string).put("status", 1).getMap();
                paraListMap.add(map2);
                Map<String, Object> params = MapUtils.put("param", paraListMap).getMap();
                sd2.setData(params);

                try {
                    log.info("移库上架任务id->" + string + ",更新移库上架状态入参->" + JsonUtils.toString(sd2));
                    String searchForJsonStr = integrationApi.searchForJsonStr(sd2);
                    searchForJsonStr = StringUtils.replaceBlank(searchForJsonStr);
                    JsonNode parse = JsonUtils.parse(searchForJsonStr);
                    boolean has = parse.has("success");
                    if (has) {
                        boolean asBoolean = parse.get("success").asBoolean();
                        if (!asBoolean) {
                            log.info("移库上架任务id->" + string + ",更新移库上架状态入参失败->" + JsonUtils.toString(sd2));
                            throw new BizException("移库上架任务id->" + string + ",更新移库上架状态入参失败->" + JsonUtils.toString(sd2));
                        }
                    }
                } catch (Exception e) {
                    log.error("移库上架任务id->" + string + ",更新移库上架状态失败,原因->" + e.getMessage());
                    throw new BizException("移库上架任务id->" + string + ",更新移库上架状态失败,原因->" + e.getMessage());
                }
            }
        }

        return null;
    }

    @Override
    @Transactional
    public Object outReceive(Map<String, Object> map) {
        // 接收移库出任务回传
        IntegrationSearchData sd = new IntegrationSearchData();
        sd.setInterfaceId(sendWms);
        Map<String, Object> data2 = MapUtils.put("type", "1").getMap(); // 只搜索类型为1的 移库出
        sd.setData(data2);
        JsonNode parse = null;
        try {
            String string = JsonUtils.toString(sd);
            log.info("索取移库下架回传请求参数->" + string);
            String searchForJsonStr = integrationApi.searchForJsonStr(sd);
            searchForJsonStr = StringUtils.replaceBlank(searchForJsonStr);
            log.info("索取移库下架回传,返回数据->" + searchForJsonStr);
            parse = JsonUtils.parse(searchForJsonStr);
            if (parse.has("success") && !parse.get("success").asBoolean()) {
                log.error("索取移库下架回传失败!原因->" + searchForJsonStr);
                throw new BizException("索取移库下架回传失败!原因->" + searchForJsonStr);
            }
        } catch (Exception e) {
            log.error("索取移库下架回传失败!原因->" + e.getMessage());
            throw new BizException("索取移库下架回传失败!原因->" + e.getMessage());
        }
        if (ObjectUtils.isNull(parse)) {
            throw new BizException("索取移库下架回传数据为空!");
        }
        JsonNode jsonNode = parse.get("data");
        if (jsonNode == null) {
            log.info("回传数据为空");
            return null;
        }
        Set<String> lineIds = new HashSet<>();
        for (JsonNode jsonNode2 : jsonNode) {
            JsonNode jsonNode3 = jsonNode2.get("TASK_ID");
            if (jsonNode3 == null) {
                continue;
            }
            String taskId = jsonNode2.get("TASK_ID").asText();
            if (StringUtils.isEmpty(taskId) || taskId.equals("null")) {
                continue;
            }
            lineIds.add(taskId);
        }
        if (ObjectUtils.isEmpty(lineIds)) {
            log.info("回传数据为空");
            return null;
        }
        log.info("移库下架确认数据->" + lineIds.toString());
        // 执行移库确认逻辑
        MoveConfirmParamsVO vo = new MoveConfirmParamsVO();
        vo.setMoveFlag(2);
        vo.setLineIds(lineIds.toArray(new String[lineIds.size()]));
        this.confirmMove(vo);

        // 调用接口平台,更新回传状态
        for (String pickTaskDId : lineIds) {
            IntegrationSearchData sd2 = new IntegrationSearchData();
            sd2.setInterfaceId(updateAgvStatus);

            Map<String, Object> map22 = MapUtils.put("task_id", pickTaskDId).getMap();
            //			Map<String, Object> map22 = MapUtils.put("task_id", pickTaskDId).getMap();
            Map<String, Object> paramMap = MapUtils.put("param", map22).getMap();
            sd2.setData(paramMap);
            try {
                String string = JsonUtils.toString(sd2);
                log.info("移库下架回传状态请求数据->" + string);
                String searchForJsonStr2 = integrationApi.searchForJsonStr(sd2);
                searchForJsonStr2 = StringUtils.replaceBlank(searchForJsonStr2);
                JsonNode parse2 = JsonUtils.parse(searchForJsonStr2);
                if (parse2.has("success") && !parse2.get("success").asBoolean()) {
                    log.error("移库下架回传状态失败!原因->" + searchForJsonStr2);
                    throw new BizException("移库下架回传状态失败!原因->" + searchForJsonStr2);
                }
            } catch (Exception e) {
                log.error("移库下架回传状态失败!原因->" + e.getMessage());
                throw new BizException("移库下架回传状态失败!原因->" + e.getMessage());
            }
        }

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveStereo(ValidList<MoveStereoAddVO> list) {

        check4AddStereo(list);

        String[] containerCodes = list.stream().map(MoveStereoAddVO::getContainerCode).toArray(String[]::new);
        MoveStereoAddVO vo = list.get(0);
        String warehouseId = vo.getWarehouseId();
        List<WmsStereoH> stereoHS = stereoHMapper.getStereoByConditions(warehouseId, null, containerCodes, StereoHdrStatus.INITIAL);
        if (CollUtil.isNotEmpty(stereoHS)) {
            throw new BizException(String.format("开票失败，托盘【%s】正在工作，请稍候再试", stereoHS.get(0).getContainerCode()));
        }

        final String billNo = IdUtil.getId(STEREO_BILL_NO);
        List<WmsStereoH> stereoH = list.stream().map(v -> moveConvert.generateStereoH(v, userService.getUser())).collect(Collectors.toList());

        List<WmsStereoD> dList = Lists.newArrayList();
        List<StockLotLockVO> stockLocks = Lists.newArrayList();
        for (WmsStereoH h : stereoH) {
            //这里列表只有2个数据，如果以后这里没限制列表数量就需要基础服务提供批量查询的接口
            Page<DeviceContainerItemStockDTO> itemPage = RestMsgHelper.unpack(stockResource.getDeviceContainerItemStockPage(h.getEnterpriseId(), h.getWarehouseId(), h.getCargoOwnerId(), h.getContainerCode(), 1, 10000000));
            List<DeviceContainerItemStockDTO> stocks = itemPage.getList();
            Assert.notEmpty(stocks, "获取托盘信息失败");

            dList.addAll(stocks.stream().map(t -> moveConvert.generateStereoD(t, h)).collect(Collectors.toList()));
            StockLotLockVO lockVO = new StockLotLockVO();
            lockVO.setIds(stocks.stream().map(DeviceContainerItemStockDTO::getId).collect(Collectors.toList()));
//            lockVO.setStockStatus("合托锁定");

            stockLocks.add(lockVO);
        }

        stereoH.forEach(s -> {
            s.setBillNo(billNo);
            stereoHMapper.save(s);
        });

        dList.forEach(t -> {
            stereoDMapper.save(t);
        });

        // 预占
        for (StockLotLockVO lock : stockLocks) {
            RestMessage restMessage = stockResource.lockStockLot(lock);
            Assert.isTrue(restMessage.isSuccess(), restMessage.getMessage());
        }

        callEis4SaveStereo(stereoH, dList);
    }

    private void callEis4SaveStereo(List<WmsStereoH> stereoH, List<WmsStereoD> dList) {
        List<EisPushTaskVO> callOutTaskVOS = generateEisPushTaskVO(stereoH, dList);
//FIXME:调接口封装参数
//        mqProducer.outBoundRequest(callOutTaskVOS);
    }

    private InvStockMove getInvStockMove4Stereo(DeviceContainerItemStockDTO d, WmsStereoH h) {
        InvStockMove stockMove = new InvStockMove();
        stockMove.setBillId(h.getHeaderId());
        stockMove.setWarehouseId(h.getWarehouseId());
        stockMove.setLineBillId(d.getId());

        //h.getContainerCode()
        // 移入
        //InvStockMoveDetail moveDetail = new InvStockMoveDetail();
        //moveDetail.setBatchId(d.getBatchId());
        //moveDetail.setInLocatorId(d.getInLocatorId());
        //moveDetail.setQuantity(d.getPieceQuantity());
        //moveDetail.setInContainerCode(d.getInContainerCode());
        //moveDetail.setInGoodsCaseCode(d.getInGoodsCaseCode());
        //moveDetail.setComments(d.getComments());
        //moveDetail.setInStockStatus(d.getInStockStatus());
        //// 移出
        //moveDetail.setOutLocatorId(d.getOutLocatorId());
        //moveDetail.setQuantity(d.getPieceQuantity());
        //moveDetail.setOutContainerCode(d.getOutContainerCode());
        //moveDetail.setOutGoodsCaseCode(d.getOutGoodsCaseCode());
        //moveDetail.setComments(d.getComments());
        //moveDetail.setOutStockStatus(d.getOutStockStatus());
        //if (!StringUtils.isBlank(d.getInStockSubStatus())) {
        //    moveDetail.setInStockSubStatus(d.getInStockSubStatus());
        //}
        //stockMove.setDetails(Collections.singletonList(moveDetail));
        return stockMove;
    }

    private List<EisPushTaskVO> generateEisPushTaskVO(List<WmsStereoH> hList, List<WmsStereoD> dList) {
        List<EisPushTaskVO> list = new ArrayList<>(dList.size());
        for (int i = 0; i < dList.size(); i++) {
            WmsStereoD d = dList.get(i);
            EisPushTaskVO vo = new EisPushTaskVO();
            WmsStereoH stereoH = hList.stream().filter(h -> h.getHeaderId().equals(d.getHeaderId())).findFirst().orElse(null);
            if (stereoH != null) {
                vo.setTaskId(stereoH.getBillNo());
                vo.setWarehouseId(stereoH.getWarehouseId());
                vo.setOwnerId(stereoH.getCargoOwnerId());
                vo.setContainerNo(stereoH.getContainerCode());
            }
            vo.setSeqNo(i + "");
            vo.setWorkType(EisWorkType.WT_COMBINED);
            vo.setBusinessType(EisBusinessType.BT_COMBINED);
            vo.setItemId(d.getItemId());
            vo.setQty(d.getQuantity());
            vo.setLotId(d.getBatchId());
            vo.setPriority(0);
            vo.setWmslocationCode(EIS_LOCATION);
            vo.setMsgId(d.getDetailId());

            list.add(vo);
        }
        return list;
    }

    @Override
    public Page<StereoDtlVO> getStereoListByContainerCode(String warehouseId, String containerCode) {
        List<StereoDtlVO> list = stereoDMapper.getStereoListByContainerCode(warehouseId, containerCode);

        if (CollUtil.isNotEmpty(list)) {

            String[] itemIds = list.stream().map(StereoDtlVO::getItemId).toArray(String[]::new);
            String[] batchIds = list.stream().map(StereoDtlVO::getBatchId).toArray(String[]::new);

            List<ItemBaseInfoVO> items = RestMsgHelper.unpack(itemResource.getItemBaseList(itemIds));
            List<BatchNumberListVO> batchNumbers = RestMsgHelper.unpack(stockResource.getBatchNumberBasicInfo(batchIds));

            for (StereoDtlVO d : list) {
                if (CollUtil.isNotEmpty(items)) {
                    items.stream().filter(i -> i.getItemId().equals(d.getItemId())).findFirst().ifPresent(i -> {
                        d.setManufacturerName(i.getManufacturerName());
                        d.setSpecification(i.getSpecification());
                        d.setCaseConversionRate(String.valueOf(i.getCaseConversionRate()));
                    });
                }
                if (CollUtil.isNotEmpty(batchNumbers)) {
                    batchNumbers.stream().filter(b -> b.getBatchId().equals(d.getBatchId())).findFirst().ifPresent(b -> {
                        d.setBatchExpiredDateShow(b.getBatchExpiredDateShow());
                        d.setBatchProductionDateShow(b.getBatchProductionDateShow());
                    });
                }
            }
        }
        return PageUtils.getPage(list);
    }

    @Override
    public Object getMovePrintData(MovePrintDataVO vo) {

        UserDTO userDTO = userService.getUser();
        List<Map<String, Object>> tableList = Lists.newArrayList();
        for (MovePrintDataDtlVO printData : vo.getDtls()) {
            tableList.add(BeanUtil.beanToMap(printData));
        }

        PrintData pd = new PrintData();
        Map<String, Object> head = new HashMap<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        head.put("printDate", sdf.format(new Date()));
        head.put("cargoOwnerName", vo.getCargoOwnerName());
        head.put("warehouseName", vo.getWarehouseName());
        head.put("operBy", userDTO.getUsername());
        List<ReportData> rdList = Lists.newArrayList();
        ReportData rd = new ReportData();
        rd.setHead(head);
        rd.setTable(tableList);
        rdList.add(rd);
        pd.setData(rdList);
        return pd;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TxTransaction(isStart = true)
    public String confirmStereo(ConfirmStereoVO vo) {
        WmsStereoD srcStereoD = stereoDMapper.findById(vo.getDetailId(), WmsStereoD.class);
        Assert.notNull(srcStereoD, "当前任务不存在");

        if (srcStereoD.getIsDeleted().equals(Integer.valueOf(YES))) {
            throw new BizException(String.format("当前物料【%s】不存在,请检查", srcStereoD.getItemName()));
        }

        if (srcStereoD.getQuantity().compareTo(vo.getActQuantity()) < 0) {
            throw new BizException("转移数量不能大于当前数量");
        }

        WmsStereoH srcStereoH = stereoHMapper.findById(srcStereoD.getHeaderId(), WmsStereoH.class);
        Assert.notNull(srcStereoH, "当前合托单不存在");

        if (vo.getToContainerCode().equals(srcStereoH.getContainerCode())) {
            throw new PrologException("相同的托盘不能做合托操作");
        }

        List<WmsStereoH> stereoHS = stereoHMapper.getStereoByConditions(srcStereoH.getWarehouseId(), srcStereoH.getBillNo(), null, StereoHdrStatus.INITIAL);
        Assert.notEmpty(stereoHS, "该合托单不存在或已完成");

        WmsStereoH destStereoH = stereoHS.stream().filter(s -> !s.getHeaderId().equals(srcStereoH.getHeaderId())).findFirst().orElse(null);
        Assert.notNull(destStereoH, "目标合托单不存在");

        if (!vo.getToContainerCode().equals(destStereoH.getContainerCode())) {
            throw new PrologException("目标托盘与目标合托单的托盘号不符");
        }

        List<WmsStereoD> destStereoDs = stereoDMapper.getListByHeadIds(new String[]{destStereoH.getHeaderId()});
        Assert.notEmpty(destStereoDs, "获取目标托盘信息失败");

        UserDTO user = userService.getUser();

        //目标物料是否与源相同
        List<WmsStereoD> destList = destStereoDs.stream().filter(d -> srcStereoD.getItemId().equals(d.getItemId()) && srcStereoD.getBatchId().equals(d.getBatchId()) && srcStereoD.getStockStatus().equals(d.getStockStatus())).collect(Collectors.toList());

        DateTime now = DateUtil.date();
        List<com.prolog.upcloud.base.wh.operate.vo.StockLotLockVO> unLockList = new ArrayList<>(2);
        List<StockLotLockVO> lockList = new ArrayList<>(2);
        //目标托盘上没有相同物料，目标托盘增新，源托盘更减
        if (destList.size() == 0) {
            WmsStereoD destStereoD = moveConvert.copyWmsStereoD(srcStereoD);
            destStereoD.setActQuantity(vo.getActQuantity());
            destStereoD.setQuantity(vo.getActQuantity());
            destStereoD.setHeaderId(destStereoH.getHeaderId());
            destStereoD.setGmtCreate(now);
            destStereoD.setCreator(user.getId());
            destStereoD.setCreatorName(user.getNickname());
            destStereoD.setModifier(user.getId());
            destStereoD.setModifierName(user.getNickname());
            destStereoD.setGmtModified(DateUtil.date());

            srcStereoD.setActQuantity(vo.getActQuantity().negate());
            srcStereoD.setQuantity(srcStereoD.getQuantity().subtract(vo.getActQuantity()));
            srcStereoD.setToContainerCode(vo.getToContainerCode());
            srcStereoD.setModifier(user.getId());
            srcStereoD.setModifierName(user.getNickname());
            srcStereoD.setGmtModified(DateUtil.date());
            srcStereoD.setIsDeleted(Integer.valueOf(YES));

            unLockList.add(getUnLockStockLotLockVO(ListUtil.toList(srcStereoD, destStereoD)));

            stereoDMapper.update(srcStereoD);
            stereoDMapper.save(destStereoD);
        } else {

            WmsStereoD destStereoD = destList.get(0);
            BigDecimal actQuantity = destStereoD.getActQuantity() == null ? BigDecimal.ZERO : destStereoD.getActQuantity();
            destStereoD.setActQuantity(actQuantity.add(vo.getActQuantity()));
            destStereoD.setQuantity(destStereoD.getQuantity().add(vo.getActQuantity()));
            destStereoD.setModifier(user.getId());
            destStereoD.setModifierName(user.getNickname());
            destStereoD.setGmtModified(DateUtil.date());

            BigDecimal srcActQuantity = srcStereoD.getActQuantity() == null ? BigDecimal.ZERO : srcStereoD.getActQuantity();
            srcStereoD.setActQuantity(srcActQuantity.subtract(vo.getActQuantity()));
            srcStereoD.setQuantity(srcStereoD.getQuantity().subtract(vo.getActQuantity()));
            srcStereoD.setToContainerCode(vo.getToContainerCode());
            srcStereoD.setModifier(user.getId());
            srcStereoD.setModifierName(user.getNickname());
            srcStereoD.setGmtModified(DateUtil.date());

            if (srcStereoD.getQuantity().compareTo(BigDecimal.ZERO) == 0) {
                srcStereoD.setIsDeleted(MsgTemplate.YES);
            }

            if (destStereoD.getQuantity().compareTo(BigDecimal.ZERO) != 0) {
                destStereoD.setIsDeleted(MsgTemplate.NO);
            }

            unLockList.add(getUnLockStockLotLockVO(ListUtil.toList(srcStereoD, destStereoD)));

            stereoDMapper.update(srcStereoD);
            stereoDMapper.update(destStereoD);
        }

        //解锁
        for (com.prolog.upcloud.base.wh.operate.vo.StockLotLockVO unLock : unLockList) {
            RestMsgHelper.unpack(stockResource.unLockStockLot(unLock));
        }

        List<InvStockMove> invStock = Collections.singletonList(stockConvert.generateStockStereo(srcStereoD, srcStereoH, destStereoH, vo.getActQuantity()));
        log.info("moveLocatorStock.params:{}", JSONObject.toJSONString(invStock));
        RestMsgHelper.unpack(stockResource.moveLocatorStock(invStock));

        //锁定
        for (com.prolog.upcloud.base.wh.operate.vo.StockLotLockVO unLock : unLockList) {
            StockLotLockVO lock = new StockLotLockVO();
            lock.setIds(unLock.getIds());
//            lock.setStockStatus("合托锁定");

            RestMsgHelper.unpack(stockResource.lockStockLot(lock));
        }

        freeContainerNo(srcStereoH, srcStereoD);

        return srcStereoD.getItemName();
    }

    /**
     * 判断是否存在可用库存，如果没有就释放
     */
    private void freeContainerNo(WmsStereoH srcStereoH, WmsStereoD srcStereoD) {

        List<WmsInvStockLot> stocks = RestMsgHelper.unpack(stockResource.getStockListByIds(new String[]{srcStereoD.getLockStockLotId()}));
        if (CollUtil.isNotEmpty(stocks)) {

            boolean isNotClear = stocks.stream().anyMatch(s -> s.getQuantity().compareTo(BigDecimal.ZERO) != 0);

            if (isNotClear) {
                return;
            }
        }

        ContainerQueryVO containerVO = new ContainerQueryVO();
        containerVO.setContainerCode(srcStereoH.getContainerCode());
        containerVO.setEnterpriseId(srcStereoH.getEnterpriseId());
        containerVO.setWarehouseId(srcStereoH.getWarehouseId());

        RestMsgHelper.unpack(warehouseClient.release(containerVO));
    }

    private com.prolog.upcloud.base.wh.operate.vo.StockLotLockVO getUnLockStockLotLockVO(List<WmsStereoD> list) {
        String[] ids = list.stream().map(WmsStereoD::getLockStockLotId).toArray(String[]::new);
        Assert.notEmpty(ids, "获取库存信息失败");

        List<WmsInvStockLot> stocks = RestMsgHelper.unpack(stockResource.getStockListByIds(ids));
        Assert.notEmpty(stocks, "获取托盘信息失败");

        com.prolog.upcloud.base.wh.operate.vo.StockLotLockVO unLockVO = new com.prolog.upcloud.base.wh.operate.vo.StockLotLockVO();
        unLockVO.setIds(stocks.stream().map(WmsInvStockLot::getId).collect(Collectors.toList()));
        unLockVO.setStockStatus("合托解锁");
        return unLockVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void finishStereo(FinishStereoVO vo) {

        List<WmsStereoH> stereoHs = stereoHMapper.getStereoByConditions(vo.getWarehouseId(), vo.getBillNo(), null, StereoHdrStatus.INITIAL);
        Assert.notEmpty(stereoHs, "该合托单不存在或已完成");

        String[] headIds = stereoHs.stream().map(WmsStereoH::getHeaderId).toArray(String[]::new);
        List<WmsStereoD> stereoDList = stereoDMapper.getListByHeadIds(headIds);
        Assert.notEmpty(stereoDList, "获取合托明细失败");

        com.prolog.upcloud.base.wh.operate.vo.StockLotLockVO unLock = getUnLockStockLotLockVO(stereoDList);

        List<ContainerConfirmDto> params = generateEisTaskFinishedConfirmVOS(stereoHs, stereoDList);

        DateTime now = DateUtil.date();
        UserDTO user = userService.getUser();
        stereoHs.forEach(h -> {
            h.setStatus(StereoHdrStatus.FINISH);
            h.setModifier(user.getId());
            h.setModifierName(user.getNickname());
            h.setGmtModified(now);
            stereoHMapper.update(h);
        });

        RestMsgHelper.unpack(stockResource.unLockStockLot(unLock));
//FIXME:调接口封装参数
//        params.forEach(mqProducer::operationComplete);
    }

    /**
     * 封装EIS完成出库任务回库参数
     *
     * @param stereoHs
     * @param stereoDList
     * @return
     */
    private List<ContainerConfirmDto> generateEisTaskFinishedConfirmVOS(List<WmsStereoH> stereoHs, List<WmsStereoD> stereoDList) {
        //这里合托从业务上来说只会有2个托盘操作，所以这里写死是2个回传推送消息
        List<ContainerConfirmDto> params = new ArrayList<>(2);

        String[] batchIds = stereoDList.stream().map(WmsStereoD::getBatchId).distinct().toArray(String[]::new);
        List<BatchNumberListVO> batchNumbers = RestMsgHelper.unpack(stockResource.getBatchNumberBasicInfo(batchIds));

        for (WmsStereoH h : stereoHs) {
            ContainerConfirmDto dto = new ContainerConfirmDto();
            dto.setContainerNo(h.getContainerCode());

            List<WmsStereoD> dList = stereoDList.stream().filter(d -> h.getHeaderId().equals(d.getHeaderId())).collect(Collectors.toList());
            Assert.notEmpty(dList, "根据合托单号{}获取合托任务失败", h.getBillNo());

            List<EisConfirmSubVo> subList = generateTaskFinishedConfirmSubListVO(h, dList, batchNumbers);
            Assert.notEmpty(subList, "封装EIS参数失败");

            dto.setSubList(subList);
            params.add(dto);
        }
        return params;
    }

    private List<EisConfirmSubVo> generateTaskFinishedConfirmSubListVO(WmsStereoH stereoH, List<WmsStereoD> stereoDList, List<BatchNumberListVO> batchNumbers) {
        List<EisConfirmSubVo> list = new ArrayList<>(stereoDList.size());

        for (WmsStereoD d : stereoDList) {
            EisConfirmSubVo vo = new EisConfirmSubVo();
            vo.setWarehouseId(stereoH.getWarehouseId());
            vo.setOwnerId(stereoH.getCargoOwnerId());
            vo.setTaskId(stereoH.getBillNo());
            vo.setSeqNo(d.getDetailId());
            vo.setWorkType(EisWorkType.WT_COMBINED);
            vo.setBusinessType(EisBusinessType.BT_COMBINED);
            vo.setItemId(d.getItemId());
            vo.setWmslocationCode(EIS_LOCATION);
            vo.setMsgId(d.getDetailId());

            list.add(vo);
        }
        return list;
    }


    /**
     * 前置校验
     *
     * @param list 列表
     */
    private void check4AddStereo(ValidList<MoveStereoAddVO> list) {
        if (CollUtil.isNotEmpty(list) && list.size() == 2) {
            return;
        }
        throw new BizException("合托操作必须勾选两个托盘号进行合托");
    }

}
