package com.prolog.cs.biz.inbound.service.impl.task;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.codingapi.tx.annotation.TxTransaction;
import com.esotericsoftware.minlog.Log;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Lists;
import com.prolog.common.RestMsgHelper;
import com.prolog.common.util.DateUtils;
import com.prolog.cs.basic.item.model.BaseMtlItemMst;
import com.prolog.cs.basic.warehouse.model.BaseWmsWarehouseLocator;
import com.prolog.cs.basic.warehouse.model.BaseWmsWhContainer;
import com.prolog.cs.basic.warehouse.model.vo.ContainerQueryVO;
import com.prolog.cs.basic.warehouse.model.vo.WhLocatorQueryVO;
import com.prolog.cs.biz.control.model.vo.LocatorQueryVO;
import com.prolog.cs.biz.inbound.constant.BizConstant;
import com.prolog.cs.biz.inbound.dao.rcv.RcvHeadersMapper;
import com.prolog.cs.biz.inbound.dao.rcv.RcvLinesMapper;
import com.prolog.cs.biz.inbound.dao.rcv.RcvLinesProfilesMapper;
import com.prolog.cs.biz.inbound.dao.rqc.RqcLinesMapper;
import com.prolog.cs.biz.inbound.dao.task.TaskInstockDetailMapper;
import com.prolog.cs.biz.inbound.dto.PageParam;
import com.prolog.cs.biz.inbound.dto.print.InstockTaskPrintDTO;
import com.prolog.cs.biz.inbound.dto.rcv.RcvHDTO;
import com.prolog.cs.biz.inbound.dto.rcv.RcvLinesDTO;
import com.prolog.cs.biz.inbound.dto.rqc.RqcLinesDTO;
import com.prolog.cs.biz.inbound.dto.task.TaskInstockDetailDTO;
import com.prolog.cs.biz.inbound.model.rcv.RcvHeaders;
import com.prolog.cs.biz.inbound.model.rcv.RcvLines;
import com.prolog.cs.biz.inbound.model.rcv.RcvLinesProfiles;
import com.prolog.cs.biz.inbound.model.rqc.RqcLines;
import com.prolog.cs.biz.inbound.model.task.TaskInstockDetail;
import com.prolog.cs.biz.inbound.remote.*;
import com.prolog.cs.biz.inbound.service.impl.BaseServiceImpl;
import com.prolog.cs.biz.inbound.service.impl.CommonRemoteService;
import com.prolog.cs.biz.inbound.service.impl.strategy.WhAreaCategoryContext;
import com.prolog.cs.biz.inbound.service.task.TaskInstockDetailService;
import com.prolog.cs.biz.inbound.utils.ObjectUtils;
import com.prolog.cs.biz.inbound.utils.UserUtils;
import com.prolog.cs.biz.inbound.vo.InsockTaskRcvQueryVO;
import com.prolog.cs.biz.inbound.vo.task.TaskQueryVO;
import com.prolog.entity.vo.ContainerTailgatingRequestVO;
import com.prolog.entity.vo.InBoundRequestItemVO;
import com.prolog.entity.vo.InBoundRequestVO;
import com.prolog.enums.InBoundRequestEnum;
import com.prolog.framework.authority.core.dto.UserDTO;
import com.prolog.framework.beans.BeanUtils;
import com.prolog.framework.bz.common.integration.IntegrationApi;
import com.prolog.framework.bz.common.integration.IntegrationSearchData;
import com.prolog.framework.bz.common.integration.IntegrationSearchException;
import com.prolog.framework.bz.common.print.PrintData;
import com.prolog.framework.bz.common.print.ReportData;
import com.prolog.framework.bz.common.search.SearchApi;
import com.prolog.framework.bz.core.collection.ValidList;
import com.prolog.framework.common.message.RestMessage;
import com.prolog.framework.core.exception.BizException;
import com.prolog.framework.core.exception.ParameterException;
import com.prolog.framework.core.pojo.Page;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Order;
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.*;
import com.prolog.inte.EisHandler;
import com.prolog.upcloud.base.inventory.dto.InstockItemInfo;
import com.prolog.upcloud.base.inventory.vo.InvStockCancel;
import com.prolog.upcloud.base.inventory.vo.InvStockIn;
import com.prolog.upcloud.base.inventory.vo.InvStockInDetail;
import com.prolog.upcloud.base.strategy.domain.core.Strategy;
import com.prolog.upcloud.base.strategy.dto.StrategyDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.prolog.cs.biz.inbound.constant.InboundBizConstant.EIS_DEVICE_AREA_CODES;
import static com.prolog.cs.biz.inbound.constant.InboundBizConstant.EIS_PAPER_AREA_CODES;

@Service
public class TaskInstockDetailServiceImpl extends BaseServiceImpl<TaskInstockDetail>
        implements TaskInstockDetailService {

    @Resource
    private RcvLinesProfilesMapper rcvLinesProfilesMapper;

    @Resource
    private TaskInstockDetailMapper mapper;

    @Resource
    private RqcLinesMapper rqcLinesMapper;

    @Resource
    private RcvLinesMapper rcvLinesMapper;

    @Resource
    private UserUtils userUtils;

    @Resource
    private ControlRemoteCaller controlRemoteCaller;

    @Resource
    private StrategyClient strategyClient;

    @Resource
    private BaseInventoryRemoteCaller baseInventoryRemoteCaller;

    @Resource
    private BaseWmsWarehouseCaller baseWmsWarehouseCaller;

    @Resource
    private BaseItemRemoteCaller itemClient;

    @Resource
    private RcvHeadersMapper rcvHeadersMapper;

    @Value("${prolog.wms.enableAutoGenLocatorByInventory:false}")
    private boolean enableAutoGenLocatorByInventory; // 是否自动获取货位(废弃)

    @Value("${prolog.wms.enableAutoGenLocatorByLimit:false}")
    private boolean enableAutoGenLocatorByLimit; // 自动上架时是否使用货位限定获取货位

    @Value("${prolog.wms.enableDefaultAreaCategory:false}")
    private boolean enableDefaultAreaCategory; // 上架前是否使用默认库别

    @Value("${prolog.wms.enableVaildateAreaCategory:true}")
    private boolean enableVaildateAreaCategory; // 上架前是否校验库别

    @Value("${prolog.wms.enableAsyncRecommendLocator:false}")
    private boolean enableAsyncRecommendLocator; // 是否异步推荐货位

    @Resource
    private IntegrationApi integrationApi;

    @Value("${prolog.wms.instockTransforId:284}")
    private String instockTransforId;

    @Value("${prolog.wms.enablePush:false}")
    private Boolean enablePush;

    @Resource
    private SearchApi searchApi;

    private Executor executor = Executors.newFixedThreadPool(2);

    @Value("${prolog.sleepTimes:1000}") // 休眠1s
    private Long sleepTimes;


    @Resource
    private CommonRemoteService commonRemoteService;

    @Resource
    private TaskInstockDetailMapper taskInstockDetailMapper;

    @Resource
    private EisHandler eisHandler;

    private static Logger logger = LoggerFactory.getLogger(TaskInstockDetailServiceImpl.class);

    @Override
    public Object add(TaskInstockDetailDTO taskInstockDetailDTO) {
        // TODO vaildate
        UserDTO userDTO = userUtils.getUserDTO();
        TaskInstockDetail taskInstockDetail = new TaskInstockDetail();
        BeanUtils.copyProperties(taskInstockDetailDTO, taskInstockDetail);
        this.vaildateModel(taskInstockDetail, false, userDTO);
        this.buildField(taskInstockDetail, false, userDTO);
        this.mapper.save(taskInstockDetail);
        return "";
    }

    @Override
    public Object edit(String pkId, TaskInstockDetailDTO taskInstockDetailDTO) {
        // TODO
        UserDTO userDTO = userUtils.getUserDTO();
        TaskInstockDetail taskInstockDetail = new TaskInstockDetail();
        BeanUtils.copyProperties(taskInstockDetailDTO, taskInstockDetail);
        this.vaildateModel(taskInstockDetail, true, userDTO);
        this.buildField(taskInstockDetail, true, userDTO);
        this.mapper.updateWithValidate(taskInstockDetail,
                p -> ObjectUtils.isNotNull(p) && ObjectUtils.isNotNull(p.getValue()));
        return "";
    }

    @Override
    @Transactional
    public Object delIds(List<String> pkIdList) {
        // TODO
        pkIdList.stream().filter(p -> canEdit(p)).forEach(p -> this.mapper.deleteById(p, TaskInstockDetail.class));
        return "";
    }

    @Override
    public Object deletePkId(String pkId) {
        // TODO
        if (canEdit(pkId))
            throw new BizException("can not del");
        this.mapper.deleteById(pkId, TaskInstockDetail.class);
        return "";
    }

    @Override
    public Page<TaskInstockDetailDTO> findList(TaskInstockDetailDTO taskInstockDetailDTO, PageParam pageParam) {
        Criteria criteria = this.buildPageParam(pageParam, true, null, TaskInstockDetail.class);
        Restriction r = buildCondition(taskInstockDetailDTO);
        Restriction and1 = Restrictions.eq("status", "1"); // 必须可用
        Restriction condition = Restrictions.and(r, and1);
        criteria.setRestriction(condition);
        this.startPage(pageParam);
        List<TaskInstockDetail> list = this.mapper.findByCriteria(criteria);
        Page<TaskInstockDetail> page = PageUtils.getPage(list);
        Page<TaskInstockDetailDTO> page2 = this.buildPage(page, (p) -> {
            TaskInstockDetailDTO target = new TaskInstockDetailDTO();
            BeanUtils.copyProperties(p, target);
            return target;
        });
        return page2;
    }

    @Override
    @Transactional
    public Object batchAdd(List<TaskInstockDetailDTO> taskInstockDetailDTOList) {
        // TODO
        UserDTO userDTO = userUtils.getUserDTO();
        for (TaskInstockDetailDTO taskInstockDetailDTO : taskInstockDetailDTOList) {
            TaskInstockDetail taskInstockDetail = new TaskInstockDetail();
            BeanUtils.copyProperties(taskInstockDetailDTO, taskInstockDetail);
            this.vaildateModel(taskInstockDetail, false, userDTO);
            this.buildField(taskInstockDetail, false, userDTO);
            this.mapper.save(taskInstockDetail);
        }
        return "";
    }

    @Override
    @Transactional
    public Object batchEdit(List<TaskInstockDetailDTO> taskInstockDetailDTOList) {
        // TODO
        UserDTO userDTO = userUtils.getUserDTO();
        for (TaskInstockDetailDTO taskInstockDetailDTO : taskInstockDetailDTOList) {
            TaskInstockDetail taskInstockDetail = new TaskInstockDetail();
            BeanUtils.copyProperties(taskInstockDetailDTO, taskInstockDetail);
            this.vaildateModel(taskInstockDetail, true, userDTO);
            this.buildField(taskInstockDetail, true, userDTO);
            this.mapper.updateWithValidate(taskInstockDetail,
                    p -> ObjectUtils.isNotNull(p) && ObjectUtils.isNotNull(p.getValue()));
        }
        return "";
    }

    @Override
    public List<TaskInstockDetailDTO> findAll(TaskInstockDetailDTO taskInstockDetailDTO) {
        // TODO
        Criteria criteria = Criteria.forClass(TaskInstockDetail.class);
        TaskInstockDetailDTO condition = taskInstockDetailDTO;
        Restriction r = buildCondition(condition);
        Restriction and1 = Restrictions.eq("status", "1"); // 必须可用
        Restriction restriction = Restrictions.and(r, and1);
        criteria.setRestriction(restriction);
        Order order = Order.newInstance().desc("gmtModified");
        criteria.setOrder(order);
        List<TaskInstockDetail> list = this.mapper.findByCriteria(criteria);
        List<TaskInstockDetailDTO> collect = Optional.ofNullable(list).orElse(Collections.emptyList()).stream()
                .map(p -> {
                    TaskInstockDetailDTO target = new TaskInstockDetailDTO();
                    BeanUtils.copyProperties(p, target);
                    return target;
                }).collect(Collectors.toList());
        return collect;
    }

    private TaskInstockDetail findById(String id) {
        return this.mapper.findById(id, TaskInstockDetail.class);
    }

    private void vaildateModel(TaskInstockDetail taskInstockDetail, Boolean editFlag, UserDTO userDTO) {
        if (!editFlag) {
            // TODO add
        } else {
            // TODO
        }
    }

    private void buildField(TaskInstockDetail taskInstockDetail, Boolean editFlag, UserDTO userDTO) {
        if (!editFlag) {
            // TODO add
        } else {
            // TODO edit
        }
        vaildateModelRepeated(taskInstockDetail, editFlag, userDTO);
    }

    private void vaildateModelRepeated(TaskInstockDetail taskInstockDetail, Boolean editFlag, UserDTO userDTO) {
        if (!editFlag) {
            // TODO add
        } else {
            // TODO
        }
    }

    private boolean canEdit(String pkId) {
        boolean flag = true;
        TaskInstockDetail taskInstockDetail = this.findById(pkId);
        AssertUtils.notNull(taskInstockDetail, "model empty!");
        // TODO
        if (true)
            flag = false;
        return flag;
    }

    private Restriction buildCondition(TaskInstockDetailDTO taskInstockDetailDTO) {
        Restriction r0 = Restrictions.eq("enterpriseId", userUtils.getEnterpriseId());
        if (ObjectUtils.isNull(taskInstockDetailDTO))
            return r0;

        // 构建查询条件
        String batchNum = taskInstockDetailDTO.getBatchNum();
        String cargoOwnerId = taskInstockDetailDTO.getMtlOwnerId();
        String warehouseId = taskInstockDetailDTO.getWarehouseId();
        String rcvHeaderId = taskInstockDetailDTO.getRcvHeaderId();
        String rcvLineId = taskInstockDetailDTO.getRcvLineId();
        String itemId = taskInstockDetailDTO.getItemId();

        Restriction r1 = StringUtils.isBlank(cargoOwnerId) ? null : Restrictions.eq("cargoOwnerId", cargoOwnerId);
        Restriction r2 = StringUtils.isBlank(warehouseId) ? null : Restrictions.eq("warehouseId", warehouseId);
        Restriction r3 = StringUtils.isBlank(rcvHeaderId) ? null : Restrictions.eq("rcvHeaderId", rcvHeaderId);
        Restriction r4 = StringUtils.isBlank(rcvLineId) ? null : Restrictions.eq("rcvLineId", rcvLineId);
        Restriction r5 = StringUtils.isBlank(itemId) ? null : Restrictions.eq("itemId", itemId);
        Restriction r6 = StringUtils.isBlank(batchNum) ? null : Restrictions.like("batchNum", "%" + batchNum + "%");

        return Restrictions.and(r0, r1, r2, r3, r4, r5, r6);
    }

    @Override
    public Object excuteTask(List<RcvLinesDTO> rcvLinesDTOList, String mtlOwnerId, String warehouseId) {
        // 收货直接上架成功
        UserDTO userDTO = userUtils.getUserDTO();
        Date now = new Date();
        // 合格不合格 由上架行操作者确认

        for (RcvLinesDTO rcvLinesDTO : rcvLinesDTOList) {
            TaskInstockDetail detail = new TaskInstockDetail();
            BeanUtils.copyProperties(rcvLinesDTO, detail);
            this.buildField(detail, false, userDTO, now);
            detail.setWarehouseId(warehouseId);
            detail.setMtlOwnerId(mtlOwnerId);
            detail.setActiveFlag("0"); // 默认允许作业
            detail.setAsnHeaderId(rcvLinesDTO.getAsnHeaderId());
            detail.setAsnLineId(rcvLinesDTO.getAsnLineId());
            detail.setSrcHeaderId(rcvLinesDTO.getSrcHeaderId());
            detail.setSrcLineId(rcvLinesDTO.getSrcLineId());
            detail.setStatus("1");
            // TODO 调用远程推荐库位接口,添加
            String itemId = detail.getItemId();
            String whLocator = getWhLocator(itemId, warehouseId, mtlOwnerId);
            detail.setWhLocatorId(whLocator);
            detail.setStatus("1");
            detail.setDataVersion(0);
            this.mapper.save(detail);
        }
        return true;
    }

    /**
     * 收货不经过质检直接上架逻辑
     */
    @Override
    @Transactional
    public Object excuteTask(List<RcvLinesDTO> rcvLinesDTOList, String mtlOwnerId, String warehouseId, boolean isAuto) {

        Set<String> collect = rcvLinesDTOList.stream().filter(p -> !StringUtils.isBlank(p.getWhLocatorId()))
                .map(p -> p.getWhLocatorId()).collect(Collectors.toSet());
        Map<String, List<Map<String, Object>>> locatorMap2 = null;
        try {
            if (!ObjectUtils.isNull(collect)) {
                RestMessage<List<Map<String, Object>>> locationDetailInfos = baseWmsWarehouseCaller
                        .getLocationDetailInfos(collect.toArray(new String[collect.size()]));
                if (!ObjectUtils.isNull(locationDetailInfos) && !ObjectUtils.isNull(locationDetailInfos.getData())) {
                    List<Map<String, Object>> data = locationDetailInfos.getData();
                    locatorMap2 = data.stream()
                            .collect(Collectors.groupingBy(p -> (String) p.get("warehouseLocatorId")));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        UserDTO userDTO = userUtils.getUserDTO();
        Date now = new Date();
        List<TaskInstockDetail> detailsList = new ArrayList<>();
        int i = 0;
        for (RcvLinesDTO rcvLinesDTO : rcvLinesDTOList) {
            i++;
            TaskInstockDetail detail = new TaskInstockDetail();
            BeanUtils.copyProperties(rcvLinesDTO, detail);

            // 加入货位数据
            String whLocatorCode = detail.getWhLocatorCode();
            String whLocatorId = rcvLinesDTO.getWhLocatorId();
            detail.setWhLocatorId(whLocatorId);
            if (!StringUtils.isBlank(whLocatorId) && StringUtils.isBlank(whLocatorCode)) {
                if (!ObjectUtils.isNull(locatorMap2) && !StringUtils.isBlank(rcvLinesDTO.getWhLocatorId())) {
                    List<Map<String, Object>> list = locatorMap2.get(whLocatorId);
                    if (ObjectUtils.isNotNull(list)) {
                        Map<String, Object> map = list.get(0);
                        detail.setWhLocatorCode((String) map.get("warehouseLocatorCode"));
                        detail.setWhAreaCode((String) map.get("warehouseAreaCode"));
                        detail.setWhAreaCategoryCode((String) map.get("warehouseAreaCategoryCode"));
                    }
                }
            }

            // 质检的结论不可为空
            String stockStatus = rcvLinesDTO.getStockStatus();
            AssertUtils.isNotBlank(stockStatus, String.format("第[%d]行的结论为空,请检查!", i));
            detail.setResult(stockStatus);
            this.buildField(detail, false, userDTO, now);
            if (StringUtils.isBlank(warehouseId)) {
                warehouseId = rcvLinesDTO.getWarehouseId();
            }
            if (StringUtils.isBlank(mtlOwnerId)) {
                mtlOwnerId = rcvLinesDTO.getMtlOwnerId();
            }
            detail.setWarehouseId(warehouseId);
            detail.setMtlOwnerId(mtlOwnerId);
            detail.setActiveFlag("0"); // 默认允许作业
            String whAreaCategory = rcvLinesDTO.getWhAreaCategoryCode();
            if (!StringUtils.isBlank(whAreaCategory)) {
                detail.setWhAreaCategoryCode(whAreaCategory);
            }

            if (ArrayUtil.contains(EIS_DEVICE_AREA_CODES, detail.getWhAreaCategoryCode())) {
                detail.setAttribute2("Device");
                detail.setEisPushStatus("1");
            }
            if (ArrayUtil.contains(EIS_PAPER_AREA_CODES, detail.getWhAreaCategoryCode())) {
                detail.setAttribute2("Paper");
                detail.setEisPushStatus("1");
            }

            detail.setAsnHeaderId(rcvLinesDTO.getAsnHeaderId());
            detail.setAsnLineId(rcvLinesDTO.getAsnLineId());
            detail.setSrcHeaderId(rcvLinesDTO.getSrcHeaderId());
            detail.setSrcLineId(rcvLinesDTO.getSrcLineId());

            BigDecimal pieceQty = rcvLinesDTO.getPieceQty();
            BigDecimal conversionRate = rcvLinesDTO.getConversionRate();
            // 计算qty,保证准确性
            if (pieceQty == null)
                pieceQty = BigDecimal.ZERO;
            if (conversionRate == null || BigDecimal.ZERO.compareTo(conversionRate) == 0)
                conversionRate = BigDecimal.ONE;
            BigDecimal divide = pieceQty.divide(conversionRate, 4, RoundingMode.DOWN);
            detail.setQty(divide);
            detail.setDataVersion(0);
            detail.setStatus("1");
            // 执行货位覆盖操作
            excuteSetAreaCategoryOrLocator(rcvLinesDTO, detail, now);
            // TODO 上架标签,这里先用雪花代替,以后使用其他逻辑获取,虚拟
            detail.setInstockLabel(String.valueOf(SnowFlakeUtils.getId()));

            detail.setRefType(0);
            this.mapper.save(detail);
            detailsList.add(detail);
        }
        // 异步推荐库别/库位
//        excuteRecommend(detailsList, userDTO, isAuto);

        //推送设备库任务到EIS
        sendInBoundRequest(detailsList);

        // 打印上架标签
        if (ObjectUtils.isNull(detailsList))
            throw new BizException("当前验收明细无需打印上架标签!请检查!");
        List<String> ids = detailsList.stream().map(p -> p.getTaskInboundId()).collect(Collectors.toList());
        return excuteAssembleInstockPrintData(ids, userDTO, false);
    }

    /**
     * EIS入库请求
     */
    public void sendInBoundRequest(List<TaskInstockDetail> list) {

        if (CollUtil.isEmpty(list)) {
            return;
        }

        String[] rcvHeaderIds = list.stream().map(TaskInstockDetail::getRcvHeaderId).distinct().toArray(String[]::new);
        List<RcvHeaders> headers = rcvHeadersMapper.findByIds(RcvHeaders.class, rcvHeaderIds, null);
        Assert.notEmpty(headers, "获取收货单失败");

        Map<String, List<TaskInstockDetail>> deviceMaps = list.stream().filter(d -> ArrayUtil.contains(EIS_DEVICE_AREA_CODES, d.getWhAreaCategoryCode())).collect(Collectors.groupingBy(TaskInstockDetail::getContainerCode));

        if (MapUtil.isNotEmpty(deviceMaps)) {

            List<TaskInstockDetail> deviceList = deviceMaps.values().stream().flatMap(Collection::stream).collect(Collectors.toList());

            Set<String> itemIds = deviceList.stream().map(TaskInstockDetail::getItemId).collect(Collectors.toSet());
            List<BaseMtlItemMst> items = RestMsgHelper.getData(itemClient.getListByIds(itemIds));
            Assert.notEmpty(items, "获取物料信息失败");

            List<InBoundRequestVO> eisDeviceParams = deviceList.stream().collect(Collectors.groupingBy(TaskInstockDetail::getContainerCode)).entrySet().stream().map(entry -> packInBoundRequestVO(entry, headers, items)).collect(Collectors.toList());

            if (CollUtil.isNotEmpty(eisDeviceParams)) {
                logger.info("eisHandler.inBoundRequest:{}", JSONObject.toJSONString(eisDeviceParams));
                eisHandler.inBoundRequest(eisDeviceParams);
            }
            list.removeAll(deviceList);
        }

        Map<String, List<TaskInstockDetail>> paperMaps = list.stream().filter(d -> ArrayUtil.contains(EIS_PAPER_AREA_CODES, d.getWhAreaCategoryCode())).collect(Collectors.groupingBy(TaskInstockDetail::getContainerCode));

        if (MapUtil.isNotEmpty(paperMaps)) {
            List<ContainerTailgatingRequestVO> eisPaperParams = paperMaps.values().stream().flatMap(Collection::stream).collect(Collectors.groupingBy(TaskInstockDetail::getContainerCode)).entrySet().stream().map(entry -> packContainerTailgatingRequestVO(entry, headers)).collect(Collectors.toList());

            if (CollUtil.isNotEmpty(eisPaperParams)) {
                logger.info("eisHandler.containerTailgatingRequest:{}", JSONObject.toJSONString(eisPaperParams));
                eisHandler.containerTailgatingRequest(eisPaperParams);
            }
        }

    }

    private InBoundRequestVO packInBoundRequestVO(Entry<String, List<TaskInstockDetail>> entry, List<RcvHeaders> headers, List<BaseMtlItemMst> items) {

        String containerNo = entry.getKey();
        List<TaskInstockDetail> dtls = entry.getValue();
        TaskInstockDetail d = dtls.get(0);
        RcvHeaders header = headers.stream().filter(h -> h.getRcvHeaderId().equals(d.getRcvHeaderId())).findFirst().orElse(null);
        Assert.notNull(header, "获取收货单失败");
        //1采购订单，2销售退回单，6直通入库单
        boolean isPoNorm = header.getRcvType().equals("1");

        InBoundRequestVO vo = new InBoundRequestVO();
        vo.setMsgId(IdUtil.fastSimpleUUID());
        vo.setWarehouseId(d.getWarehouseId());
        vo.setEnterpriseId(d.getEnterpriseId());
        vo.setContainerNo(containerNo);
        vo.setTaskId(d.getTaskInboundId());
        vo.setWorkType(isPoNorm ? InBoundRequestEnum.PO_NORM.getWorkType() : InBoundRequestEnum.PO_SORETURN.getWorkType());
        vo.setBusinessType(isPoNorm ? InBoundRequestEnum.PO_NORM.getBusinessType() : InBoundRequestEnum.PO_SORETURN.getBusinessType());
        vo.setWmsLocationCode("MJK".equals(d.getWhAreaCategoryCode())?"MJZ":d.getWhAreaCategoryCode());
        vo.setOriginalArea("");
        vo.setOriginalLocation("");
        vo.setTargetArea("");
        vo.setTargetLocation("");
        List<InBoundRequestItemVO> subList = dtls.stream().map(i -> {
            InBoundRequestItemVO itemVO = new InBoundRequestItemVO();
            BaseMtlItemMst itemMst = items.stream().filter(item -> item.getItemId().equals(i.getItemId())).findFirst().orElse(null);
            Assert.notNull(itemMst, "获取物料ID{}失败", i.getItemId());

            itemVO.setOwnerId(i.getMtlOwnerId());
            itemVO.setItemId(i.getItemId());
            itemVO.setLotId(i.getBatchId());
            itemVO.setPackId(itemMst.getBaseMtlItemUomConversionsList().size() > 0 ? itemMst.getBaseMtlItemUomConversionsList().get(0).getItemUomId() : "");
            itemVO.setQty(i.getPieceQty().toString());
            itemVO.setAttrStr1("");
            itemVO.setAttrStr2("");
            itemVO.setAttrStr3("");
            itemVO.setAttrStr4("");
            itemVO.setAttrStr5("");
            return itemVO;
        }).collect(Collectors.toList());
        vo.setSubList(subList);
        return vo;
    }

    private ContainerTailgatingRequestVO packContainerTailgatingRequestVO(Entry<String, List<TaskInstockDetail>> entry, List<RcvHeaders> headers) {

        String containerNo = entry.getKey();
        List<TaskInstockDetail> dtls = entry.getValue();
        TaskInstockDetail d = dtls.get(0);
        RcvHeaders header = headers.stream().filter(h -> h.getRcvHeaderId().equals(d.getRcvHeaderId())).findFirst().orElse(null);
        Assert.notNull(header, "获取收货单失败");
        //1采购订单，2销售退回单，6直通入库单
        boolean isPoNorm = header.getRcvType().equals("1");

        ContainerTailgatingRequestVO vo = new ContainerTailgatingRequestVO();
        vo.setMsgId(IdUtil.fastSimpleUUID());
        vo.setWarehouseId(d.getWarehouseId());
        vo.setEnterpriseId(d.getEnterpriseId());
        vo.setContainerNo(containerNo);
        vo.setTaskId(d.getTaskInboundId());
        vo.setWorkType(isPoNorm ? InBoundRequestEnum.PO_NORM.getWorkType() : InBoundRequestEnum.PO_SORETURN.getWorkType());
        vo.setBusinessType(isPoNorm ? InBoundRequestEnum.PO_NORM.getBusinessType() : InBoundRequestEnum.PO_SORETURN.getBusinessType());
        vo.setWmsLocationCode(d.getWhAreaCategoryCode());
        vo.setOriginalArea("");
        vo.setOriginalLocation("");
        vo.setTargetArea(getTargetArea4EisByAreaCategoryCode(d.getWhAreaCategoryCode()));
        vo.setTargetLocation("");
        return vo;
    }

    private String getTargetArea4EisByAreaCategoryCode(String whAreaCategoryCode) {
        if ("ZJK".equals(whAreaCategoryCode)) {
            return "ZJK01";
        }
        if ("ZYK".equals(whAreaCategoryCode)) {
            return "ZYYP";
        }
        if ("WLK".equals(whAreaCategoryCode)) {
            return "WL";
        }
        if ("NHK".equals(whAreaCategoryCode)) {
            return "NG01";
        }
        return "";
    }

    @Override
    public Object excuteAssembleInstockPrintData(List<String> ids, UserDTO userDTO, Boolean reFlag) {
        // 打印上架标签
        TaskQueryVO queryVO2 = new TaskQueryVO();
        queryVO2.setIncloudIds(ids); // 加入关联的rqcLine查询
        List<TaskInstockDetailDTO> findList = mapper.findList(queryVO2);
        logger.info("上架任务数" + findList.size());

        // 调用searchApi获取打印模板
        String warehouseId = findList.get(0).getWarehouseId();
        Map<String, Object> queryMap = new HashMap<>();
        queryMap.put("warehouseId", warehouseId);
        queryMap.put("tempType", "SJ_BQ"); // 可查询字典表灵活些
        List<Map> searchMap = null;
        try {
            logger.info("远程调用查询服务找寻打印模板配置,入参->", JsonUtils.toString(queryMap));
            searchMap = this.searchApi.searchForMap("getPrintTemplate", queryMap);
        } catch (Exception e) {
            logger.info("远程调用查询服务找寻打印模板配置失败,原因{}", e);
//				throw new BizException("远程调用查询服务找寻打印模板配置失败,请检查!");
        }
        String printSolution1 = "";
        String printUrl = "";
        if (!ObjectUtils.isNull(searchMap)) {
            Map map = searchMap.get(0);
            if (!ObjectUtils.isNull(map)) {
                printSolution1 = (String) map.get("tempname");
                printUrl = (String) map.get("temppath");
            } else {
                logger.info("查询服务找寻打印模板配置为空!");
                return null;
            }
        } else {
            logger.info("查询服务找寻打印模板配置为空!");
            return null;
        }

        PrintData pd = new PrintData();
        pd.setCount(1); // 默认1份
        pd.setPreview(false); // 是否预览
        pd.setPrintName(null); // 打印机名称
        pd.setPrintSolution(printSolution1); // 打印方案
        pd.setUrl(printUrl); // 打印模板url
        List<ReportData> reportDataList = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyyMMdd");
        for (TaskInstockDetailDTO taskInstockDetail : findList) {
            String itemIsPieceOrCase = taskInstockDetail.getItemIsPieceOrCase();
            if (StringUtils.isBlank(itemIsPieceOrCase))
                itemIsPieceOrCase = "0"; // 默认整散合一

            BigDecimal pieceQty = taskInstockDetail.getPieceQty();
            if (pieceQty == null)
                pieceQty = BigDecimal.ZERO;
            BigDecimal conversionRate = taskInstockDetail.getConversionRate();
            if (conversionRate == null || conversionRate.compareTo(BigDecimal.ZERO) == 0)
                conversionRate = BigDecimal.ONE;
            // 计算出整件,零散
            BigDecimal caseQty = pieceQty.divide(conversionRate, 0, RoundingMode.DOWN); // 整件
            BigDecimal multiply = caseQty.multiply(conversionRate);
            BigDecimal oddPieceQty = pieceQty.subtract(multiply); // 零散
//				if (BigDecimal.ZERO.compareTo(caseQty) == 0 && BigDecimal.ZERO.compareTo(oddPieceQty) == 0)
            if (BigDecimal.ZERO.compareTo(caseQty) == 0)
                continue; // 无整件,无需打印
            if (BigDecimal.ZERO.compareTo(caseQty) == -1 && BigDecimal.ZERO.compareTo(oddPieceQty) == -1)
                continue; // 有整有散件,无需打印
            if (itemIsPieceOrCase.equals("1") && (BigDecimal.ZERO.compareTo(caseQty) == 0
                    || (BigDecimal.ZERO.compareTo(caseQty) == -1 && BigDecimal.ZERO.compareTo(oddPieceQty) == -1)))
                // 整散需分开 且 (无整件 或者 有整件也有散件)无需打印
                continue;
            // 组装待打印处理的数据 TODO
            InstockTaskPrintDTO printDTO = new InstockTaskPrintDTO();
            BeanUtils.copyProperties(taskInstockDetail, printDTO);
            printDTO.setDrawer(userDTO.getUsername());
            String saleBillType = printDTO.getSaleBillType();
            String orgName = printDTO.getOrgName();
            String bizUser = printDTO.getBizUser();
            if (StringUtils.isBlank(saleBillType)) {
                printDTO.setSaleBillType("");
            }
            if (StringUtils.isBlank(orgName)) {
                printDTO.setOrgName("");
            }
            if (StringUtils.isBlank(bizUser)) {
                printDTO.setBizUser("");
            }
            String categoryName = taskInstockDetail.getCategoryName();
            printDTO.setItemCategory(categoryName);
            String rqcUser = taskInstockDetail.getRcvUser();
            printDTO.setRcvUser(rqcUser);
            printDTO.setCategoryName(categoryName);
            if (reFlag) {
                printDTO.setReFlag("重");
            }

            String showExpiredDate = taskInstockDetail.getShowExpiredDate();
            if (StringUtils.isBlank(showExpiredDate)) {
                Date batchExpiredDate = taskInstockDetail.getBatchExpiredDate();
                if (batchExpiredDate != null) {
                    showExpiredDate = sdf2.format(batchExpiredDate);
                }
            }
            printDTO.setExpiredDate(showExpiredDate);
            String showProductionDate = taskInstockDetail.getShowProductionDate();
            if (StringUtils.isBlank(showProductionDate)) {
                Date batchProductionDate = taskInstockDetail.getBatchProductionDate();
                if (batchProductionDate != null) {
                    showProductionDate = sdf2.format(batchProductionDate);
                }
            }
            printDTO.setProductionDate(showProductionDate);

            String mtlOwnerName2 = taskInstockDetail.getMtlOwnerName();
            printDTO.setMtlOwnerName(mtlOwnerName2);
            String mtlOwnerName3 = printDTO.getMtlOwnerName();
            int indexOf = mtlOwnerName3.indexOf("有限");
            if (indexOf != -1) {
                String substring = mtlOwnerName3.substring(0, indexOf);
                printDTO.setMtlOwnerName(substring);
            }
            // 调整库别名称
            excuteHandleAreaCategory(taskInstockDetail, printDTO);
            // TODO 调整显示生产日期/有效期至
            ReportData reportData = new ReportData();
            @SuppressWarnings("unchecked")
            Map<String, Object> head = ObjectUtils.convertBean2MapForIngoreserialVersionUID(printDTO);
            reportData.setHead(head);
            reportDataList.add(reportData);
        }
        logger.info("应打印标签数:" + reportDataList.size());
        pd.setData(reportDataList);
        return pd;
    }

    private void excuteHandleAreaCategory(TaskInstockDetailDTO taskInstockDetail, InstockTaskPrintDTO printDTO) {
        // 把库别编码变为名称
        String whAreaCategoryCode = taskInstockDetail.getWhAreaCategoryCode();
        if (StringUtils.isBlank(whAreaCategoryCode))
            return;
        // 应该远程调用公共服务获取库别名称,这里先用常量代替
        switch (whAreaCategoryCode) {
            case "ZCQ":
                printDTO.setWhAreaCategoryName("暂存区");
                break;
            case "LHK":
                printDTO.setWhAreaCategoryName("零货库");
                break;
            case "ZJK":
                printDTO.setWhAreaCategoryName("整件库");
                break;
            case "LTK":
                printDTO.setWhAreaCategoryName("立体库");
                break;
            case "BHK":
                printDTO.setWhAreaCategoryName("备货库");
                break;
            case "NHK":
                printDTO.setWhAreaCategoryName("不合格库");
                break;
            case "THK":
                printDTO.setWhAreaCategoryName("退货库");
                break;
            case "PDK":
                printDTO.setWhAreaCategoryName("盘点库");
                break;
            case "DYK":
                printDTO.setWhAreaCategoryName("待验库");
                break;
            case "HYK":
                printDTO.setWhAreaCategoryName("整散合一库");
                break;
            case "CLK":
                printDTO.setWhAreaCategoryName("待处理库");
                break;
            case "ADJ":
                printDTO.setWhAreaCategoryName("调整库");
                break;
            case "DGK":
                printDTO.setWhAreaCategoryName("代管库");
                break;
            case "YKQ":
                printDTO.setWhAreaCategoryName("越库区");
                break;
            case "KRK":
                printDTO.setWhAreaCategoryName("空入库");
                break;
            case "XTK":
                printDTO.setWhAreaCategoryName("箱体库");
                break;
            case "ZDK":
                printDTO.setWhAreaCategoryName("直调库");
                break;
            default:
                break;
        }
    }

    private void excuteSetAreaCategoryOrLocator(RcvLinesDTO rcvLinesDTO, TaskInstockDetail detail, Date now) {
        String rcvType = rcvLinesDTO.getRcvType();

        // 2022.4.2新加
        if (BizConstant.HOSTED_IN.equals(rcvType)) {
            // 默认代管库别,强制
            detail.setWhAreaCategoryCode(BizConstant.DGK);
            detail.setInventoryStatus("5");
        }
        if (BizConstant.WAIT_CHECK_IN.equals(rcvType)) {
            // 默认代管库别,强制
            String inventoryOpType = rcvLinesDTO.getInventoryOpType();
            if (StringUtils.isBlank(inventoryOpType) || !"1".equals(inventoryOpType)) {
                detail.setWhAreaCategoryCode(BizConstant.DGK);
                detail.setInventoryStatus("3");
            }
        }
        // 2022.4.2新加

        Date batchExpiredDate = rcvLinesDTO.getBatchExpiredDate();
        Long subtract = null;
        if (!ObjectUtils.isNull(batchExpiredDate)) {
            subtract = DateUtils.subtract(now, batchExpiredDate, ChronoUnit.DAYS);
        }
        // 处理库存状态 需要结合收货类型来处理
        String stockStatus = rcvLinesDTO.getStockStatus();
        // 库存状态，1合格，2不合格3待检4退货
        switch (stockStatus) {
            case "0":
                // 质检合格
                detail.setInventoryStatus("1"); // 库存状态合格
                break;
            case "1":
                // 质检不合格
                detail.setInventoryStatus("2"); // 库存状态为退货
                break;
            default:
                // 待处理
                detail.setInventoryStatus("3"); // 库存状态待处理
                break;
        }

        // 退货入库效期处理
//		if (subtract != null && subtract <= 0 && (rcvType.equals(BizConstant.SALE_RETURN_IN)
//				|| rcvType.equals(BizConstant.ALLOT_IN) || rcvType.equals(BizConstant.MOVE_IN))) {
//			// 销退/调拨/移库过期处理
//			// 过期了,入不合格库
//			// 无需双人质检,直接推荐
//			detail.setWhAreaCategoryCode("THK");
//			// 库存状态为不合格
//			detail.setInventoryStatus("4"); // 库存状态不合格
//		}
        // 其他类型的入库效期处理

    }

    @Transactional
    @Override
    public Object excuteTask(List<RqcLinesDTO> rqcLinesList, String conclusion, boolean isAuto) {

        Set<String> locatorIds = rqcLinesList.stream().filter(p -> !StringUtils.isBlank(p.getWhLocatorId()))
                .map(p -> p.getWhLocatorId()).collect(Collectors.toSet());
        Map<String, List<Map<String, Object>>> locatorMap2 = null;
        try {
            if (!ObjectUtils.isNull(locatorIds)) {
                RestMessage<List<Map<String, Object>>> locationDetailInfos = baseWmsWarehouseCaller
                        .getLocationDetailInfos(locatorIds.toArray(new String[locatorIds.size()]));
                if (!ObjectUtils.isNull(locationDetailInfos) && !ObjectUtils.isNull(locationDetailInfos.getData())) {
                    List<Map<String, Object>> data = locationDetailInfos.getData();
                    locatorMap2 = data.stream()
                            .collect(Collectors.groupingBy(p -> (String) p.get("warehouseLocatorId")));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 质检上架 直接走上架逻辑
        UserDTO userDTO = userUtils.getUserDTO();
        Date now = new Date();
        switch (conclusion) {
            case "0":
                // 合格
                break;
            case "1":
                // 不合格
                break;
            default:
                // 默认合格
                break;
        }
        List<TaskInstockDetail> detailsList = new ArrayList<>();
        int i = 0;
        WhLocatorQueryVO queryVO = new WhLocatorQueryVO();
        queryVO.setEnterpriseId(userDTO.getEnterpriseId());
        List<BaseWmsWarehouseLocator> locators = RestMsgHelper.getData(baseWmsWarehouseCaller.getList(queryVO));
        Assert.notEmpty(locators, "获取货位信息失败");

        for (RqcLinesDTO rqcLinesDTO : rqcLinesList) {
            i++;
            Date rqcDate = rqcLinesDTO.getRqcDate();
            if (rqcDate != null) {
                now = rqcDate;
            }
            TaskInstockDetail detail = new TaskInstockDetail();
            BeanUtils.copyProperties(rqcLinesDTO, detail);

            // 加入货位数据
            String whLocatorCode = detail.getWhLocatorCode();
            String whLocatorId = rqcLinesDTO.getWhLocatorId();
            detail.setWhLocatorId(whLocatorId);
            if (!StringUtils.isBlank(whLocatorId) && StringUtils.isBlank(whLocatorCode)) {
                if (!ObjectUtils.isNull(locatorMap2) && !StringUtils.isBlank(rqcLinesDTO.getWhLocatorId())) {
                    List<Map<String, Object>> list = locatorMap2.get(whLocatorId);
                    if (ObjectUtils.isNotNull(list)) {
                        Map<String, Object> map = list.get(0);
                        detail.setWhLocatorCode((String) map.get("warehouseLocatorCode"));
                        detail.setWhAreaCode((String) map.get("warehouseAreaCode"));
                        detail.setWhAreaCategoryCode((String) map.get("warehouseAreaCategoryCode"));
                    }
                }
            }

            // 质检的结论不可为空
            String stockStatus = rqcLinesDTO.getStockStatus();
            AssertUtils.isNotBlank(stockStatus, String.format("第[%d]行的质检结论为空,请检查!", i));
            detail.setResult(stockStatus);
            this.buildField(detail, false, userDTO, now);
            String mtlOwnerId = rqcLinesDTO.getMtlOwnerId();
            String warehouseId = rqcLinesDTO.getWarehouseId();
            detail.setWarehouseId(warehouseId);
            detail.setMtlOwnerId(mtlOwnerId);
            detail.setActiveFlag("0"); // 默认允许作业
            String whAreaCategory = rqcLinesDTO.getWhAreaCategory();
            if (!StringUtils.isBlank(whAreaCategory)) {
                detail.setWhAreaCategoryCode(whAreaCategory);
            }
            if (ArrayUtil.contains(EIS_DEVICE_AREA_CODES, detail.getWhAreaCategoryCode())) {
                detail.setAttribute2("Device");
                detail.setEisPushStatus("1");
            }
            if (ArrayUtil.contains(EIS_PAPER_AREA_CODES, detail.getWhAreaCategoryCode())) {
                detail.setAttribute2("Paper");
                detail.setEisPushStatus("1");
            }

            BaseWmsWarehouseLocator locator = locators.stream().filter(l -> l.getWarehouseAreaCategory().equals(detail.getWhAreaCategoryCode())).findFirst().orElse(null);
            Assert.notNull(locator, "根据库区获取货位失败");
            detail.setWhLocatorId(locator.getWarehouseLocatorId());
            detail.setWhLocatorCode(locator.getWarehouseLocatorCode());
            detail.setWhLocatorName(locator.getWarehouseLocatorName());

            detail.setAsnHeaderId(rqcLinesDTO.getAsnHeaderId());
            detail.setAsnLineId(rqcLinesDTO.getAsnLineId());
            detail.setSrcHeaderId(rqcLinesDTO.getSrcHeaderId());
            detail.setSrcLineId(rqcLinesDTO.getSrcLineId());

            BigDecimal pieceQty = rqcLinesDTO.getPieceQty();
            BigDecimal conversionRate = rqcLinesDTO.getConversionRate();
            // 计算qty,保证准确性
            if (pieceQty == null)
                pieceQty = BigDecimal.ZERO;
            if (conversionRate == null || BigDecimal.ZERO.compareTo(conversionRate) == 0)
                conversionRate = BigDecimal.ONE;
            BigDecimal divide = pieceQty.divide(conversionRate, 4, RoundingMode.DOWN);
            detail.setQty(divide);
//			if (null == conversionRate || conversionRate.compareTo(BigDecimal.ZERO) == 0) {
//				detail.setQty(pieceQty);
//			} else {
//				detail.setQty(pieceQty.divide(conversionRate, 4, BigDecimal.ROUND_DOWN));
//			}
            detail.setDataVersion(0);
            detail.setStatus("1");
            detail.setRqcLineId(rqcLinesDTO.getRqcLineId());
            // 执行货位覆盖操作
            excuteSetAreaCategoryOrLocator(rqcLinesDTO, detail, now);
            // TODO 上架标签,这里先用雪花代替,以后使用其他逻辑获取,虚拟
            detail.setInstockLabel(String.valueOf(SnowFlakeUtils.getId()));

//			String itemId = detail.getItemId();
//			String whLocator = getWhLocator(itemId, warehouseId, mtlOwnerId);
//			detail.setWhLocator(whLocator);
            detail.setRefType(1);

            BigDecimal oddCaseQty = pieceQty.divide(conversionRate, 0, RoundingMode.DOWN);
            BigDecimal oddPieceQty = pieceQty.subtract(oddCaseQty.multiply(conversionRate));

            if (null != oddCaseQty && oddCaseQty.compareTo(BigDecimal.ZERO) == 0 && null != oddPieceQty
                    && oddPieceQty.compareTo(BigDecimal.ZERO) == 1) {
                // 零散散
                // 暂时零散都走设备库
//				detail.setWhAreaCategoryCode(BizConstant.SBK);
            }
            //是否能入AGV库
            detail.setIsAgv(rqcLinesDTO.getIsAgv());
            this.mapper.save(detail);
            detailsList.add(detail);
        }
        // 异步推荐库别/库位
//		applicationContext.getBean(TaskInstockDetailServiceImpl.class).excuteRecommend(detailsList, userDTO);
//        excuteRecommend(detailsList, userDTO, isAuto);

        //推送设备库任务到EIS
        sendInBoundRequest(detailsList);
        return true;
    }

    private void tansfor2AGV(List<TaskInstockDetail> collect) {
        Set<String> collect2 = collect.stream().map(TaskInstockDetail::getTaskInboundId).collect(Collectors.toSet());
        TaskQueryVO vo = new TaskQueryVO();
        vo.setIncloudIds(new ArrayList<>(collect2));
        List<TaskInstockDetailDTO> findListPad = this.mapper.findList(vo);

        Map<String, Object> resultData = new HashMap<>();
        Map<String, Object> headMap = new HashMap<>();
        if (!ObjectUtils.isNull(findListPad)) {
            TaskInstockDetailDTO taskInstockDetailDTO = findListPad.get(0);
            String rcvHeaderId = taskInstockDetailDTO.getRcvHeaderId();
//			RcvHDTO findByPkId = this.rcvHeadersMapper.findByPkId(rcvHeaderId);

            String rcvUser = taskInstockDetailDTO.getRcvUser();
            String executor2 = taskInstockDetailDTO.getExecutor();
            String rqcUser = taskInstockDetailDTO.getRqcUser();
            String bizUser = taskInstockDetailDTO.getBizUser();
            String mtlOwnerId2 = taskInstockDetailDTO.getMtlOwnerId();
            String asnRemark = taskInstockDetailDTO.getAsnRemark();
            long id = SnowFlakeUtils.getId();
            String shippid = "";
            String customerId = taskInstockDetailDTO.getCustomerId();
            if (StringUtils.isBlank(shippid)) {
                shippid = customerId;
            }
            String vendorId = taskInstockDetailDTO.getVendorId();
            if (StringUtils.isBlank(shippid)) {
                shippid = vendorId;
            }
            String rcvType = taskInstockDetailDTO.getRcvType();
            rcvType = convertRcvType(rcvType);

            headMap.put("djbh", String.valueOf(id));
            headMap.put("rq", getStringDate());
            headMap.put("dwid", shippid);
            headMap.put("ry_cgy", bizUser);
            headMap.put("ry_czy", executor2);
            headMap.put("ry_shouhy", rcvUser);
            headMap.put("ry_zjy", rqcUser);
            headMap.put("shr", rcvUser);
            headMap.put("sf_wc", "0");
            headMap.put("sf_xc", "0");
            headMap.put("sf_zx", "0");
            headMap.put("djbh_sj", rcvHeaderId);
            headMap.put("rktype", "0"); // 默认为0 正常上架
            headMap.put("yzid", mtlOwnerId2);
            headMap.put("bz", asnRemark);
            int row = 0;
            List<Map> data_itemlist = new ArrayList<>();
            for (TaskInstockDetailDTO taskInstockDetailDTO2 : findListPad) {
                row++;
                String djbh = String.valueOf(id);
                Integer dj_sort = row;
                String itemId = taskInstockDetailDTO2.getItemId();
                BigDecimal pieceQty = taskInstockDetailDTO2.getPieceQty();
                BigDecimal conversionRate = taskInstockDetailDTO2.getConversionRate();
                // 数据准确性计算
                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 = taskInstockDetailDTO2.getManufacturerLotNum();
                Date batchProductionDate = taskInstockDetailDTO2.getBatchProductionDate();
                String convertStringDate1 = convertStringDate(batchProductionDate);
                Date batchExpiredDate = taskInstockDetailDTO2.getBatchExpiredDate();
                String convertStringDate2 = convertStringDate(batchExpiredDate);
                String sf_zx = "0";
                String batchId = taskInstockDetailDTO2.getBatchId();
                String result = taskInstockDetailDTO2.getResult();
                String yspd = convertRqcResult(result);
                String cgdd_sort = taskInstockDetailDTO2.getAsnLineId();
                Integer sl_ywc = 0;
                String yzid = taskInstockDetailDTO2.getMtlOwnerId();
                Integer dj_sort_sj = 1;
                String bz = taskInstockDetailDTO2.getAsnLineRemark();
                String task_id = taskInstockDetailDTO2.getTaskInboundId();
                String containerCode = taskInstockDetailDTO2.getContainerCode();

                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", yzid);
                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);
        }
        if (!ObjectUtils.isNull(resultData)) {
            IntegrationSearchData sd = new IntegrationSearchData();
            sd.setInterfaceId(instockTransforId);
            sd.setData(resultData);
            try {
                Log.info("发送agv指令数据->" + JsonUtils.toString(sd));
                String searchForJsonStr = integrationApi.searchForJsonStr(sd);
                searchForJsonStr = replaceBlank(searchForJsonStr);
                JsonNode parse = JsonUtils.parse(searchForJsonStr);
                if (parse.has("success") && !parse.get("success").asBoolean()) {
                    logger.error("入库上架下传执行agv指令失败!原因->" + searchForJsonStr);
                    throw new BizException("入库上架下传执行agv指令失败!原因->" + searchForJsonStr);
                }
            } catch (IntegrationSearchException e) {
                e.printStackTrace();
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }

        } else {
            throw new BizException("发送数据为空");
        }
    }

    public static String replaceBlank(String str) {
        String dest = "";
        if (str != null) {
            Pattern p = Pattern.compile("\\s*|\t|\r|\n");
            Matcher m = p.matcher(str);
            dest = m.replaceAll("");
        }
        return dest;
    }

    private String convertRqcResult(String result) {
        return "1";
    }

    private String convertRcvType(String rcvType) {
        String result = null;
        if (!StringUtils.isBlank(rcvType)) {
            if (BizConstant.PURCHASE_IN.equals(rcvType)) {
                result = "采购入库";
            }
            if (BizConstant.SALE_RETURN_IN.equals(rcvType)) {
                result = "销售退回";
            }
            if (BizConstant.ALLOT_IN.equals(rcvType)) {
                result = "调拨入库";
            }
            if (BizConstant.MOVE_IN.equals(rcvType)) {
                result = "移库入库";
            }
            if (BizConstant.ACCOUNT_REG_IN.equals(rcvType)) {
                result = "调账入库";
            }
            if (BizConstant.OVERSTEP_IN.equals(rcvType)) {
                result = "越库入库";
            }
            if (BizConstant.STRAIGHT_IN.equals(rcvType)) {
                result = "直调入库";
            }
            if (BizConstant.HOSTED_IN.equals(rcvType)) {
                result = "代管入库";
            }
            if (BizConstant.WAIT_CHECK_IN.equals(rcvType)) {
                result = "待检入库";
            }
            if (BizConstant.INSTRUMENT_IN.equals(rcvType)) {
                result = "器械入库";
            }
            if (BizConstant.FREE_IN.equals(rcvType)) {
                result = "赠品入库";
            }
            if (BizConstant.BLANK_ENTITY_IN.equals(rcvType)) {
                result = "空入入库(有实物)";
            }
            if (BizConstant.BLANK_IN.equals(rcvType)) {
                result = "空入入库(无实物)";
            }
            if (BizConstant.BLANK_RETURN_ENTITY_IN.equals(rcvType)) {
                result = "空退入库(有实物)";
            }
            if (BizConstant.BLANK_RETURN_IN.equals(rcvType)) {
                result = "空退入库(无实物)";
            }

            return result;
        }
        return result;
    }

    public static String getStringDate() {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = formatter.format(currentTime);
        return dateString;
    }

    public static String convertStringDate(Date date) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = formatter.format(date);
        return dateString;
    }

    private void excuteSetAreaCategoryOrLocator(RqcLinesDTO rqcLinesDTO, TaskInstockDetail detail, Date now) {
        String rcvType = rqcLinesDTO.getRcvType();
        String whAreaCategoryCode = detail.getWhAreaCategoryCode();

        // 库别的默认处理(越库,直调的处理),暂时没有开启,因为越库,直调货位目前为空
        if (enableDefaultAreaCategory && StringUtils.isBlank(whAreaCategoryCode)) {
            if (BizConstant.OVERSTEP_IN.equals(rcvType)) {
                // 默认越库区 YKQ
                detail.setWhAreaCategoryCode(BizConstant.YKQ);
            }
            if (BizConstant.STRAIGHT_IN.equals(rcvType)) {
                // 默认越库区 ZDK
                detail.setWhAreaCategoryCode(BizConstant.ZDK);
            }
        }

        // 2022.4.2新加
        if (BizConstant.HOSTED_IN.equals(rcvType)) {
            // 默认代管库别,强制
            detail.setWhAreaCategoryCode(BizConstant.DGK);
            detail.setInventoryStatus("5");
        }
        if (BizConstant.WAIT_CHECK_IN.equals(rcvType)) {
            // 默认代管库别,强制
            RcvLines findById = this.rcvLinesMapper.findById(rqcLinesDTO.getRcvLineId(), RcvLines.class);
            if (null != findById && (StringUtils.isBlank(findById.getInventoryOpType())
                    || !"1".equals(findById.getInventoryOpType()))) {
                detail.setWhAreaCategoryCode(BizConstant.DGK);
                detail.setInventoryStatus("3");
            }
        }

        // 2022.4.2新加

        Date batchExpiredDate = rqcLinesDTO.getBatchExpiredDate();
        Long subtract = null;
        if (!ObjectUtils.isNull(batchExpiredDate)) {
            subtract = DateUtils.subtract(now, batchExpiredDate, ChronoUnit.DAYS);
        }
        // 处理库存状态 TODO 需要结合收货类型来处理
        String qcResult1 = rqcLinesDTO.getQcResult1();
        String stockStatus = rqcLinesDTO.getStockStatus();
        // 库存状态，1合格，2不合格3待检4退货
        switch (stockStatus) {
            case "0":
                // 质检合格
                detail.setInventoryStatus("1"); // 库存状态合格
                break;
            case "1":
                // 质检不合格
//			detail.setInventoryStatus("4"); // 库存状态不合格
                detail.setInventoryStatus("2"); // 库存状态为退货库
//			if (rcvType.equals(BizConstant.PURCHASE_IN)) {
//				detail.setInventoryStatus("4"); // 库存状态为退货库
//			}

                break;
            default:
//			detail.setInventoryStatus("4"); // 库存状态不合格
                detail.setInventoryStatus("3"); // 库存状态待检
                break;
        }

        // 退货入库效期处理
//		if (subtract != null && subtract <= 0 && (rcvType.equals(BizConstant.SALE_RETURN_IN)
//				|| rcvType.equals(BizConstant.ALLOT_IN) || rcvType.equals(BizConstant.MOVE_IN))) {
//			// 销退/调拨/移库过期处理
//			// 过期了,入不合格库
//			// 无需双人质检,直接推荐
//			detail.setWhAreaCategoryCode("THK");
//			// 库存状态为不合格
//			detail.setInventoryStatus("4"); // 库存状态不合格
////			detail.setInventoryStatus("4"); // 库存状态不合格
//		}
        // 其他类型的入库效期处理

    }

    // @Async("taskExecutor")
    private void excuteRecommend(List<TaskInstockDetail> detailsList, UserDTO userDTO, boolean isAuto) {

        // 需要根据是否自动生成任务来区分,是否为库存服务选择货位
        if (isAuto) {
//			if (isAuto && enableAutoGenLocatorByInventory) {

        } else {
            logger.info("-------执行寻库别操作");
            boolean anyMatch = detailsList.stream().anyMatch(p -> StringUtils.isBlank(p.getWhAreaCategoryCode()));
            // 先把库别寻到
            List<LocatorQueryVO> queryVOList = null;
            if (anyMatch) {
                queryVOList = this.getWhCategory(detailsList, userDTO);
            } else {
                if (!isAuto || (isAuto && enableAutoGenLocatorByLimit)) {
                    queryVOList = new ArrayList<>();
                    String enterpriseId = userDTO.getEnterpriseId();

                    Set<String> rcvLineSet = detailsList.stream().map(p -> p.getRcvLineId())
                            .collect(Collectors.toSet());
                    List<RcvLinesProfiles> linePro = rcvLinesProfilesMapper.findByIds(RcvLinesProfiles.class,
                            rcvLineSet.toArray(new String[rcvLineSet.size()]), null);

                    for (TaskInstockDetail taskInstockDetail : detailsList) {

                        // 设备库的处理
//						String whAreaCategoryCode = taskInstockDetail.getWhAreaCategoryCode();
//						String[] split = whAreaCategoryCode.split(",");
//						String whAreaCategoryCode33 = split[0];
//						if (!StringUtils.isBlank(whAreaCategoryCode33)
//								&& whAreaCategoryCode33.equals(BizConstant.SBK)) {
//							continue;
//						}
                        RcvHeaders byId = rcvHeadersMapper.findById(taskInstockDetail.getRcvHeaderId(), RcvHeaders.class);
                        LocatorQueryVO vo = new LocatorQueryVO();
                        vo.setSegment1(byId.getRcvType());
                        String refId = taskInstockDetail.getTaskInboundId();
                        vo.setRefId(refId);
                        String enterpriseId2 = taskInstockDetail.getEnterpriseId();
                        if (StringUtils.isBlank(enterpriseId2))
                            vo.setEnterpriseId(enterpriseId);
                        else
                            vo.setEnterpriseId(enterpriseId2);
                        String warehouseId = taskInstockDetail.getWarehouseId();
                        String mtlOwnerId = taskInstockDetail.getMtlOwnerId();
                        // Step2: 组装查询货位的条件
                        InstockItemInfo info = new InstockItemInfo();
                        buildGetLocatorCondition(info, taskInstockDetail, warehouseId, mtlOwnerId, linePro);
                        vo.setItemInfo(info);
                        queryVOList.add(vo);
                    }
                } else {

                }
            }
            // 推荐货位以及更新上架任务
            Date now = new Date();
            excuteRecommendLocator(isAuto, detailsList, queryVOList, userDTO, now);

        }
    }

    private void excuteLocatorRecommend(boolean isAuto, List<TaskInstockDetail> detailsList,
                                        List<LocatorQueryVO> queryVOList, UserDTO userDTO, Date now) {
        if (!isAuto || (isAuto && enableAutoGenLocatorByLimit)) {

            // 组装数据获取货位
            try {
                this.excuteGetLocator(queryVOList, detailsList, userDTO, new Date());
            } catch (Exception e) {
                e.printStackTrace();
                logger.info("找寻推荐货位失败,原因->{}", e);
            }

        }

        // 不管是否成功!更新下上架任务
        logger.info("-------更新上架任务池!");
        this.batchEdit(detailsList, userDTO, new Date());
    }

    private void excuteRecommendLocator(boolean isAuto, List<TaskInstockDetail> detailsList,
                                        List<LocatorQueryVO> queryVOList, UserDTO userDTO, Date now) {
        if (enableAsyncRecommendLocator) {
            executor.execute(() -> {
                try {
                    Thread.sleep(sleepTimes);
                    excuteLocatorRecommend(isAuto, detailsList, queryVOList, userDTO, now);
                } catch (Exception e) {
                    logger.error("推荐货位失败,上架明细:" + detailsList.get(0).getRcvHeaderId());
                }
            });

        } else {
            excuteLocatorRecommend(isAuto, detailsList, queryVOList, userDTO, now);
        }

    }

    private List<LocatorQueryVO> getWhCategory(List<TaskInstockDetail> detailsList, UserDTO userDTO) {
        if (ObjectUtils.isNull(userDTO))
            userDTO = userUtils.getUserDTO();
        List<LocatorQueryVO> queryVOList = new ArrayList<>();

        // 调用策略获取库别 TODO
        // Step1: 远程找寻策略
        String enterpriseId = userDTO.getEnterpriseId();
        TaskInstockDetail taskInstockDetail2 = detailsList.get(0);
        String warehouseId = taskInstockDetail2.getWarehouseId();
        String mtlOwnerId = taskInstockDetail2.getMtlOwnerId();

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

        Set<String> rcvLineSet = detailsList.stream().map(p -> p.getRcvLineId()).collect(Collectors.toSet());
        List<RcvLinesProfiles> linePro = rcvLinesProfilesMapper.findByIds(RcvLinesProfiles.class,
                rcvLineSet.toArray(new String[rcvLineSet.size()]), null);

        Set itemSet = detailsList.stream().filter(dto -> !StringUtils.isBlank(dto.getItemId())).map(TaskInstockDetail::getItemId).distinct().collect(Collectors.toSet());
        List<BaseMtlItemMst> itemList = Lists.newArrayList();
        if (itemSet != null && itemSet.size() > 0) {
            RestMessage<List<BaseMtlItemMst>> itemResult = itemClient.getListByIds(itemSet);
            Assert.isFalse(itemResult == null, "质检验收获取商品信息不存在,请检查");
            if (!itemResult.isSuccess()) {
                throw BizException.newInstance("质检验收获取商品信息" + itemResult.getMessage());
            }
            itemList = itemResult.getData();
        }
        for (TaskInstockDetail taskInstockDetail : detailsList) {

            String taskInboundId = taskInstockDetail.getTaskInboundId();
            // 模拟获取假的库别
            String whAreaCategoryCode = taskInstockDetail.getWhAreaCategoryCode();
//			String defaultCode = "ZCQ"; // 默认库别 暂存库
            // 没有主动选库别
            if (StringUtils.isBlank(whAreaCategoryCode)) {
                String rcvType2 = taskInstockDetail.getRcvType();
                String hostLocatorId = taskInstockDetail.getHostLocatorId();
                if (!StringUtils.isBlank(rcvType2) && BizConstant.WAIT_CHECK_IN.equals(rcvType2)
                        && !StringUtils.isBlank(hostLocatorId)) {
                    taskInstockDetail.setWhAreaCategoryCode(BizConstant.DGK);
                    whAreaCategoryCode = BizConstant.DGK;
                }
                // 组装匹配因子
                WhAreaCategoryContext context = new WhAreaCategoryContext();
                WhAreaCategoryContext.RcvLineData rcvLineData = context.new RcvLineData();

                // 单据类型
                String rcvType = taskInstockDetail.getRcvType();
                if (StringUtils.isBlank(mtlOwnerId))
                    rcvType = "1";

                rcvLineData.setBillType(rcvType);
                rcvLineData.setDataSource("1");
                rcvLineData.setIsAgv(taskInstockDetail.getIsAgv());
                BaseMtlItemMst baseMtlItemMst = itemList.stream().filter(item -> Objects.equals(item.getItemId(), taskInstockDetail.getItemId())).findFirst().orElse(null);
//				if(baseMtlItemMst!=null && baseMtlItemMst.getBaseMtlItemWms()!=null){
//					Integer abnormal=baseMtlItemMst.getBaseMtlItemWms().getItemIsSpecialSharped();
//					rcvLineData.setStoreTemperature(abnormal.toString());
//				}

                // 是否整散
                BigDecimal pieceQty = taskInstockDetail.getPieceQty();
                BigDecimal conversionRate = taskInstockDetail.getConversionRate();
                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");
                }
                // 整散数量
                rcvLineData.setQty(oddCaseQty.doubleValue());
                rcvLineData.setOddCaseQty(oddCaseQty.doubleValue());
                rcvLineData.setOddPieceQty(oddPieceQty.doubleValue());
                if (null != oddCaseQty && oddCaseQty.compareTo(BigDecimal.ZERO) == 0 && null != oddPieceQty
                        && oddPieceQty.compareTo(BigDecimal.ZERO) == 1) {
                    // 零散散
                    rcvLineData.setIsCase("1");
//					rcvLineData.setQty(oddPieceQty.doubleValue());
                }

                if (null != oddCaseQty && oddCaseQty.compareTo(BigDecimal.ZERO) == 1 && null != oddPieceQty
                        && oddPieceQty.compareTo(BigDecimal.ZERO) == 1) {
                    // 整散
                    rcvLineData.setIsCase("2");
//					rcvLineData.setQty(pieceQty.doubleValue());
                }

                // 库存结论属性
                // 0不合格1合格
                String result = taskInstockDetail.getResult();
                String storageStatus = "0"; // 默认合格
                if (result.equals("0")) {
                    storageStatus = "0";
                } else {
                    storageStatus = "1";
                }
                rcvLineData.setStorageStatus(storageStatus);

                rcvLineData.setStockStatus(result);

                //商品大类
                rcvLineData.setCategoryId(taskInstockDetail.getCategoryId());

                context.setRcvLineData(rcvLineData);
                try {
                    logger.info("-------开始执行策略中...............!,策略条件数据->" + JsonUtils.toString(context));
                    strategy.execute(context); // 捕捉异常
                } catch (Exception e) {
                    logger.error("策略执行失败,原因->", e);
                    e.printStackTrace();
                }
                boolean completed = context.completed();
                if (completed) {
                    // 找到匹配的策略
                    logger.info("-------获取合适策略");
                    List<String> targetAreaCategoryCodes = context.getTargetAreaCategoryCodes();
                    String join = String.join(",", targetAreaCategoryCodes);
                    logger.info(String.format("上架任务[%s],推荐库别[%s]", taskInboundId, join));
                    taskInstockDetail.setWhAreaCategoryCode(join);
                }

            }
            logger.info("-------找到合适的策略成功!");
            String whAreaCategoryCode2 = taskInstockDetail.getWhAreaCategoryCode();
            String rcvType = taskInstockDetail.getRcvType();
            if (rcvType.equals(BizConstant.HOSTED_IN)) {
                whAreaCategoryCode2 = "DGK";
            }
            if (rcvType.equals(BizConstant.OVERSTEP_IN)) {
                whAreaCategoryCode2 = "YKQ";
            }
            taskInstockDetail.setWhAreaCategoryCode(whAreaCategoryCode2);
            if (StringUtils.isBlank(whAreaCategoryCode2)) {
//				logger.info("-------没有合适的库别,暂时使用默认暂存区!");
//				taskInstockDetail.setWhAreaCategoryCode(defaultCode);
                // 看是否使用默认库别
                logger.info("-------没有合适的库别!");
                throw new BizException("没有合适的库别,请选择库别,或联系管理员检查并设置上架策略!");
            }

            // 设备库的处理
            String[] split = whAreaCategoryCode2.split(",");
            String whAreaCategoryCode33 = split[0];
            if (!StringUtils.isBlank(whAreaCategoryCode33) && whAreaCategoryCode33.equals(BizConstant.SBK)) {
                continue;
            }

            LocatorQueryVO vo = new LocatorQueryVO();
            String refId = taskInstockDetail.getTaskInboundId();
            vo.setRefId(refId);
            String enterpriseId2 = taskInstockDetail.getEnterpriseId();
            if (StringUtils.isBlank(enterpriseId2))
                vo.setEnterpriseId(enterpriseId);
            else
                vo.setEnterpriseId(enterpriseId2);

            // Step2: 组装查询货位的条件
            InstockItemInfo info = new InstockItemInfo();
            buildGetLocatorCondition(info, taskInstockDetail, warehouseId, mtlOwnerId, linePro);
            vo.setItemInfo(info);
            queryVOList.add(vo);

        }
        return queryVOList;
        // TODO
        // 根据是否需要上架的标识输出打印的数据(暂时移到质检头中做数据组织处理)
    }

    private void excuteGetLocator(List<LocatorQueryVO> queryVOList, List<TaskInstockDetail> detailsList,
                                  UserDTO userDTO, Date now) {
        // Step3: 远程获取推荐库位
        RestMessage<Map<String, BaseWmsWarehouseLocator>> recommendLocatoror = null;
        try {
            logger.info("-------开始调用远程寻货位操作!--入参数:" + JsonUtils.toString(queryVOList));
            recommendLocatoror = controlRemoteCaller.getRecommendLocatoror(queryVOList);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BizException(String.format("远程调用库位推荐失败!错误原因->%s", e.getMessage()));
        }
        Map<String, BaseWmsWarehouseLocator> data = null;
        if (!ObjectUtils.isNull(recommendLocatoror) && recommendLocatoror.isSuccess()) {
            data = recommendLocatoror.getData();
            if (ObjectUtils.isNull(data))
                throw new BizException(String.format("推荐数据为空!请重试!"));
            // 看是否有数据
            int row = 0;
            for (Entry<String, BaseWmsWarehouseLocator> entry : data.entrySet()) {
                row++;
                BaseWmsWarehouseLocator value = entry.getValue();
                String key = entry.getKey();
                if (ObjectUtils.isNull(value)) {

//					throw new BizException(String.format("第[%d]行数据,来源任务id(上架任务)[%s]的推荐货位为空!", row, key));
                    logger.info(String.format("第[%d]行数据,来源任务id(上架任务)[%s]的推荐货位为空!", row, key));

                }
            }
            logger.info("-------寻货位成功!");
            // 需要更新的上架任务
//			List<TaskInstockDetail> updateList = new ArrayList<>();
            for (TaskInstockDetail taskInstockDetail : detailsList) {
                String taskInboundId = taskInstockDetail.getTaskInboundId();
                BaseWmsWarehouseLocator baseWmsWarehouseLocator = data.get(taskInboundId);
                String whLocatorId = taskInstockDetail.getWhLocatorId();
                if (ObjectUtils.isNull(baseWmsWarehouseLocator) || !StringUtils.isEmpty(whLocatorId))
                    continue;
                String warehouseLocatorId = baseWmsWarehouseLocator.getWarehouseLocatorId();
                String warehouseLocatorCode = baseWmsWarehouseLocator.getWarehouseLocatorCode();
                String warehouseLocatorName = baseWmsWarehouseLocator.getWarehouseLocatorName();
                taskInstockDetail.setWhLocatorId(warehouseLocatorId);
                taskInstockDetail.setWhLocatorCode(warehouseLocatorCode);
                taskInstockDetail.setWhLocatorName(warehouseLocatorName);
                taskInstockDetail.setWhAreaCode(baseWmsWarehouseLocator.getWarehouseAreaCode());
                taskInstockDetail.setLocatorFloorNum(baseWmsWarehouseLocator.getFloorNum());
                String whAreaCategoryCode = taskInstockDetail.getWhAreaCategoryCode();
                if (StringUtils.isBlank(whAreaCategoryCode)) {
                    taskInstockDetail.setWhAreaCategoryCode(baseWmsWarehouseLocator.getWarehouseAreaCategoryCode());
                }

                taskInstockDetail.setAttribute2(baseWmsWarehouseLocator.getPickMode()); // 加入拣货方式
//						taskInstockDetail.setWhAreaCategoryName(baseWmsWarehouseLocator.getWarehouseAreaCategory());
//				updateList.add(taskInstockDetail);
            }
            // 更新上架任务池货位相关数据
//			logger.info("-------更新上架任务池!");
//			this.batchEdit(updateList, userDTO, now);

        } else {
            // 打印日志
            // 抛出错误
            logger.info("-------远程找寻货位失败!");
            Map<String, BaseWmsWarehouseLocator> data2 = recommendLocatoror.getData();
            String string = "";
            if (data2 != null) {
                string = recommendLocatoror.getMessage() + data2.toString();
            }
            throw new BizException(String.format("远程调用库位推荐失败!错误原因->%s", string));
        }

    }

    private void buildGetLocatorCondition(InstockItemInfo info, TaskInstockDetail taskInstockDetail, String warehouseId,
                                          String mtlOwnerId, List<RcvLinesProfiles> linePro) {
        Map<String, String> collect = null;
        if (!ObjectUtils.isNull(linePro)) {
            collect = linePro.stream().collect(Collectors.toMap(RcvLinesProfiles::getRcvLineId,
                    RcvLinesProfiles::getStoreTemperature, (key1, key2) -> key2));
        }
        String itemId = taskInstockDetail.getItemId();
        String rcvLineId = taskInstockDetail.getRcvLineId();
        String rcvHeaderId = taskInstockDetail.getRcvHeaderId();
        BigDecimal pieceQty = taskInstockDetail.getPieceQty();
        String bldgCode = taskInstockDetail.getBldgCode();
        BigDecimal conversionRate = taskInstockDetail.getConversionRate();
        String vendorBatchNum = taskInstockDetail.getVendorBatchNum();
        String manufacturerLotNum = taskInstockDetail.getManufacturerLotNum();
        String goodsCaseCode = taskInstockDetail.getGoodsCaseCode();
        String containerCode = taskInstockDetail.getContainerCode();
        String categoryId = taskInstockDetail.getCategoryId();
        Date batchExpiredDate = taskInstockDetail.getBatchExpiredDate();
        Date batchProductionDate = taskInstockDetail.getBatchProductionDate();
        if (ObjectUtils.isNull(batchExpiredDate) || ObjectUtils.isNull(batchProductionDate))
            throw new ParameterException("请检查自己的批次生产日期和批次有效期!");
        String productionPlace = taskInstockDetail.getProductionPlace();
        String orgId = taskInstockDetail.getOrgId();
        String vendorId = taskInstockDetail.getVendorId();
        String erpBatchNum2 = taskInstockDetail.getErpBatchNum();
        BigDecimal piecePriceTe = taskInstockDetail.getPiecePriceTe();
        BigDecimal piecePriceTi = taskInstockDetail.getPiecePriceTi();
        BigDecimal taxRate = taskInstockDetail.getTaxRate();
        String enterpriseId = taskInstockDetail.getEnterpriseId();

        String whAreaCategoryCode2 = taskInstockDetail.getWhAreaCategoryCode();

        info.setRcvId(rcvHeaderId);
        info.setRcvLineId(rcvLineId);
        info.setWarehouseCategoryCode(whAreaCategoryCode2);
        info.setEnterpriseId(enterpriseId);
        info.setBatchExpiredDate(batchExpiredDate);
        info.setBatchProductionDate(batchProductionDate);
        info.setCargoOwnerId(mtlOwnerId);
        info.setErpBatchNum(erpBatchNum2); // 预留
        info.setItemId(itemId);
        info.setManufacturerLotNum(manufacturerLotNum);
        info.setOrgnId(orgId); // 预留
        info.setProductionPlace(productionPlace);
        info.setShelfLifeDays(Math.abs(DateUtils.subtract(batchExpiredDate, batchProductionDate, ChronoUnit.DAYS)));
        info.setPieceCostTe(piecePriceTe);
        info.setPieceCostTi(piecePriceTi);
        info.setTaxRate(taxRate);
        info.setVendorBatchNum(vendorBatchNum);
        info.setVendorId(vendorId);
        info.setWarehouseId(warehouseId);
        info.setGoodsCaseCode(goodsCaseCode);
        info.setItemPieceQty(pieceQty);
        info.setBldgCode(bldgCode);

        String storeTempture = null;
        if (!ObjectUtils.isNull(collect)) {
            storeTempture = collect.get(rcvLineId);
            if (StringUtils.isBlank(storeTempture)) {
                storeTempture = "常温";
            }
            if (!StringUtils.isBlank(storeTempture) && !storeTempture.contains("常温") && !storeTempture.contains("阴凉")
                    && !storeTempture.contains("冷藏") && !storeTempture.contains("冷冻")) {
                storeTempture = "常温";
            }
        }
        info.setStoreTemperature(storeTempture);

        info.setConversionRate(conversionRate);
        info.setContainerCode(containerCode);
        info.setItemCategoryCode(categoryId);

        info.setBillId(taskInstockDetail.getAsnHeaderId());
        info.setAsnId(taskInstockDetail.getAsnHeaderId());
        info.setLineBillId(taskInstockDetail.getAsnLineId());

        String asnHeaderId = taskInstockDetail.getAsnHeaderId();
        String taskInboundId = taskInstockDetail.getTaskInboundId();
        String batchId = taskInstockDetail.getBatchId();

        // 这里需要改变,先用结论顶替 TODO
//		String result = taskInstockDetail.getResult();
//		switch (result) {
//		case "0":
//			info.setStockStatus(StockStatusEnum.QUALIFIED.getCode());
//			break;
//		case "1":
//			info.setStockStatus(StockStatusEnum.UNQUALIFIED.getCode());
//			break;
//		case "2":
//			info.setStockStatus(StockStatusEnum.WAITING_INSPECTION.getCode());
//			break;
//		case "3":
//			info.setStockStatus(StockStatusEnum.REFUND.getCode());
//			break;
//		default:
//			info.setStockStatus(StockStatusEnum.UNQUALIFIED.getCode());
//			break;
//		}
        String inventoryStatus = taskInstockDetail.getInventoryStatus();
        // 库存状态
        info.setStockStatus(inventoryStatus);

        // 需要加入二级库存状态
        String secondStatus = taskInstockDetail.getSecondStatus();
        if (!StringUtils.isBlank(secondStatus)) {
            info.setStockStatus(secondStatus);
        }

        info.setAsnId(asnHeaderId);
        info.setRcvLineId(rcvLineId);
        info.setRcvId(rcvHeaderId);
        info.setTaskId(taskInboundId);
        info.setBatchId(batchId);

    }

    @Transactional
    public void batchEdit(List<TaskInstockDetail> detailsList, UserDTO userDTO, Date now) {
        if (ObjectUtils.isNull(detailsList))
            return;
        if (ObjectUtils.isNull(userDTO))
            userDTO = userUtils.getUserDTO();
        if (ObjectUtils.isNull(now))
            now = new Date();

        // 随机获取设备库货位
        boolean allMatch = detailsList.stream().allMatch(p -> p.getWhAreaCategoryCode().equals(BizConstant.SBK));
        Map map = null;
        if (allMatch) {
            List<String> includeAreaCategoryCodes = new ArrayList<>();
            includeAreaCategoryCodes.add(BizConstant.SBK);
            List<Map> searchForMap = null;
            try {
                searchForMap = this.searchApi.searchForMap("locator_info_list",
                        MapUtils.put("includeAreaCategoryCodes", includeAreaCategoryCodes).getMap());
            } catch (Exception e) {
                logger.error("设备库查询货位数据为空!请检查重试");
            }
            if (ObjectUtils.isNull(searchForMap)) {
                throw new BizException("设备库查询货位数据为空!请检查重试");
            }
            map = searchForMap.get(0);
        }
        for (TaskInstockDetail taskInstockDetail : detailsList) {
            String whAreaCategoryCode = taskInstockDetail.getWhAreaCategoryCode();
            if (whAreaCategoryCode.equals(BizConstant.SBK) && !ObjectUtils.isNull(map)) {
                String whLocatorId = (String) map.get("whLocatorId");
                String whLocatorCode = (String) map.get("whLocatorCode");
                String whLocatorName = (String) map.get("whLocatorName");
                Object object1 = map.get("floorNum");
                Integer floorNum = 0;
                if (object1 instanceof Double) {
                    Double parseDouble = Double.parseDouble(object1.toString());
                    floorNum = parseDouble.intValue();
                } else if (object1 instanceof String) {
                    BigDecimal bigDecimal = new BigDecimal((String) object1);
                    floorNum = bigDecimal.intValue();

                } else if (object1 instanceof Integer) {
                    floorNum = (Integer) object1;
                }

                String whAreaCode = (String) map.get("whAreaCode");

                taskInstockDetail.setWhLocatorId(whLocatorId);
                taskInstockDetail.setWhLocatorCode(whLocatorCode);
                taskInstockDetail.setWhLocatorName(whLocatorName);
                taskInstockDetail.setWhAreaCode(whAreaCode);
                taskInstockDetail.setLocatorFloorNum(floorNum);
            }

            buildField(taskInstockDetail, true, userDTO, now);
            taskInstockDetail.setInstockLabel(String.valueOf(SnowFlakeUtils.getId())); // 这里先用默认标签代替
            this.mapper.update(taskInstockDetail);
        }
    }

    /**
     * 调用推荐算法算出
     *
     * @param itemId
     * @param warehouseId
     * @param mtlOwnerId
     * @return
     */
    private String getWhLocator(String itemId, String warehouseId, String mtlOwnerId) {

//		579668851520835584
//		577549271423913984
        return "579668851520835584";
    }

    private void buildField(TaskInstockDetail detail, boolean editFlag, UserDTO userDTO, Date now) {
        // 构建系统基础字段
        Date gmtCreate = detail.getGmtCreate();
        Date gmtModified = detail.getGmtModified();
        String creator = detail.getCreator();
        String creatorName = detail.getCreatorName();
        String modifier = detail.getModifier();
        String modifierName = detail.getModifierName();
        String id = userDTO.getId();
        String enterpriseId = userDTO.getEnterpriseId();
        String username = userDTO.getUsername();

        if (null == now)
            now = new Date();
        if (!editFlag) {
            if (ObjectUtils.isNull(creator))
                detail.setCreator(id);
            if (ObjectUtils.isNull(creatorName))
                detail.setCreatorName(username);
            if (ObjectUtils.isNull(gmtCreate))
                detail.setGmtCreate(now);
            detail.setEnterpriseId(enterpriseId);

            if (ObjectUtils.isNull(detail.getIsSpilt()))
                detail.setIsSpilt(0);
            detail.setStatus("1");
            detail.setDataVersion(0);
        }
        if (ObjectUtils.isNull(modifier))
            detail.setModifier(id);
        if (ObjectUtils.isNull(modifierName))
            detail.setModifierName(username);
        if (ObjectUtils.isNull(gmtModified))
            detail.setGmtModified(now);
        vaildateModelRepeated(detail, editFlag, userDTO);
    }

    /**
     * 远程调用库存服务调整库存
     *
     * @param collect
     * @param userDTO
     */
    private void excutePreInStock(List<TaskInstockDetailDTO> collect, UserDTO userDTO) {
        ValidList<InvStockIn> invStock = new ValidList<>();
        String enterpriseId = userDTO.getEnterpriseId();
        for (TaskInstockDetailDTO info : collect) {
            InvStockIn invStockIn = new InvStockIn();
            List<InvStockInDetail> details = new ArrayList<InvStockInDetail>();
            InvStockInDetail detail = new InvStockInDetail();
            BigDecimal pieceQty = info.getPieceQty();
            String warehouseLocatorId = info.getWhLocatorId();
            String comments = info.getComments();
            String goodsCaseCode = info.getGoodsCaseCode();
            String containerCode = info.getContainerCode();
            String inventoryStatus = info.getInventoryStatus();
            // 库存状态
            detail.setStatus(inventoryStatus);

            // 二级库存状态
            String secondStatus = info.getSecondStatus();
            if (!StringUtils.isBlank(secondStatus)) {
                detail.setStatus(secondStatus);
            }

            detail.setQuantity(pieceQty);
            detail.setContainerCode(containerCode);
            detail.setGoodsCaseCode(goodsCaseCode);
            detail.setLocatorId(warehouseLocatorId);
            detail.setComments(comments);
            details.add(detail);

            // 远程库存服务需修改 taskId
            invStockIn.setDetails(details);

            BeanUtils.copyProperties(info, invStockIn);
            invStockIn.setAsnId(info.getAsnHeaderId());
            invStockIn.setAsnLineId(info.getAsnLineId());
            invStockIn.setEnterpriseId(enterpriseId);
            invStockIn.setRcvId(info.getRcvHeaderId());
            invStockIn.setRcvLineId(info.getRcvLineId());
            invStockIn.setTaskId(info.getTaskInboundId());
            invStock.add(invStockIn);
        }
        baseInventoryRemoteCaller.adjustPreStock(invStock);
    }

    private boolean canCommit(String taskId, int row, String statusResult, TaskInstockDetailDTO taskInstockDetailDTO,
                              boolean splitFlag) {
        // 数据库乐观锁
        TaskInstockDetail taskInstockDetail = this.mapper.findById(taskId, TaskInstockDetail.class);
        AssertUtils.notNull(taskInstockDetail, String.format("第[%d]行,上架任务id:[%s]查询的任务为空!", row, taskId));
        String status = taskInstockDetail.getStatus();
        if (!"1".equals(status))
            throw new BizException(String.format("第[%d]行,上架任务id:[%s]查询的任务已经执行完毕,请刷新重试!", row, taskId));

        BigDecimal pieceQty2 = taskInstockDetail.getPieceQty();
        BigDecimal pieceQty = taskInstockDetailDTO.getPieceQty();
        if (splitFlag && pieceQty2.compareTo(pieceQty) < 1) {
            throw new BizException("第[%d]行,拆分的数量不可大于等于原数量!");
        }

        Criteria criteria = new Criteria(TaskInstockDetail.class);
        Integer dataVersion = taskInstockDetail.getDataVersion();

        // 这里只需要dataVersion其实就可以了
        criteria.setRestriction(Restrictions.and(Restrictions.eq("taskInboundId", taskId),
                Restrictions.eq("dataVersion", dataVersion)));
        Map<String, Object> map = MapUtils.put("dataVersion", dataVersion + 1).getMap();
        if (!StringUtils.isBlank(statusResult)) {
            map.put("status", "0");
        }
        long count = this.mapper.updateMapByCriteria(map, criteria);
        if (0 >= count)
            return false;
        return true;
    }

    @Override
    public TaskInstockDetailDTO findByPkId(String pkId) {
        // TODO
        TaskInstockDetailDTO dto = this.mapper.findByPkId(pkId);
        AssertUtils.notNull(dto, "查询的上架任务池为空!");

        return dto;
    }

    @Override
    public Page<TaskInstockDetailDTO> extractTaskWithNativeSql(TaskQueryVO queryVO, PageParam pageParam) {
        long start = System.currentTimeMillis();
        logger.info("上架任务查询开始时间: " + start + "(ms)");
        String enterpriseId = userUtils.getEnterpriseId();
        queryVO.setEnterpriseId(enterpriseId);
//		Integer pageNum = 1;
//		Integer pageSize = 500;
        Integer pageNum = pageParam.getPageNum();
        Integer pageSize = pageParam.getPageSize();
        if (pageNum == null) {
            pageNum = 1;
        }
        if (pageSize == null) {
            pageSize = 200;
        }
        pageParam.setPageSize(pageSize);
        pageParam.setPageNum(pageNum);
        this.startPage(pageParam);
        List<TaskInstockDetailDTO> list = this.mapper.findList(queryVO);
        long end2 = System.currentTimeMillis();
        logger.info("上架任务sql查询结束时间: " + end2 + "(ms)");
        long eb2 = end2 - start;
        logger.info("sql耗时: " + eb2 + "(ms)");
        if (!ObjectUtils.isNull(list)) {
            List<BaseMtlItemMst> itemList = null;
            Set itemSet = list.stream().filter(p -> !StringUtils.isBlank(p.getItemId()))
                    .map(TaskInstockDetailDTO::getItemId).collect(Collectors.toSet());
            if (itemSet != null && itemSet.size() > 0) {
                RestMessage<List<BaseMtlItemMst>> itemResult = itemClient.getListByIds(itemSet);
                itemList = itemResult.getData();
            }
            for (TaskInstockDetailDTO dto : list) {
                if (itemList != null && itemList.size() > 0) {
                    BaseMtlItemMst baseMtlItemMst = itemList.stream().filter(t -> t.getItemId().equals(dto.getItemId()))
                            .findFirst().orElse(null);
                    if (baseMtlItemMst != null && baseMtlItemMst.getBaseMtlItemHealth() != null) {
                        dto.setFtypesCode(baseMtlItemMst.getBaseMtlItemHealth().getFtypesCode());
                    }
                }
            }
            boolean anyMatch = list.stream().anyMatch(p -> StringUtils.isBlank(p.getHostLocatorId()));
            if (anyMatch) {
                long end = System.currentTimeMillis();
                logger.info("上架任务查询结束时间: " + end + "(ms)");
                long eb = end - start;
                logger.info("耗时: " + eb + "(ms)");
                Page<TaskInstockDetailDTO> page = PageUtils.getPage(list);
                long end3 = System.currentTimeMillis();
                logger.info("转成page结束时间: " + end3 + "(ms)");
                long eb3 = end3 - end;
                logger.info("转成page耗时: " + eb3 + "(ms)");
                logger.info("整体耗时: " + (end3 - start) + "(ms)");
                return page;
            }
            Set<String> collect = list.stream().filter(p -> !StringUtils.isBlank(p.getRcvType())
//					&& BizConstant.WAIT_CHECK_IN.equals(p.getRcvType())
                            && !StringUtils.isBlank(p.getHostLocatorId())).map(TaskInstockDetailDTO::getHostLocatorId)
                    .collect(Collectors.toSet());

            if (ObjectUtils.isNotNull(collect)) {
                Map<String, Object> queryMap = new HashMap<>();
                queryMap.put("locatorIds", collect);
                List<Map> searchMap = null;
                try {
                    searchMap = searchApi.searchForMap("locator_info_list", queryMap);
                } catch (Exception e) {
                    logger.info("找寻货位信息失败,原因{}", e);
                }
                if (!ObjectUtils.isNull(searchMap)) {
                    Map<String, List<Map>> collect2 = searchMap.stream()
                            .collect(Collectors.groupingBy(e -> (String) e.get("whLocatorId")));
                    for (TaskInstockDetailDTO dto : list) {
                        String hostLocatorId = dto.getHostLocatorId();
                        if (!StringUtils.isBlank(hostLocatorId)) {
                            List<Map> list2 = collect2.get(hostLocatorId);
                            if (!ObjectUtils.isNull(list2)) {
                                Map map = list2.get(0);
                                dto.setRcvWhLocatorId(hostLocatorId);
                                dto.setRcvWhLocatorCode((String) map.get("whLocatorCode"));
//								dto.setRcvWhLocatorName((String) map.get("whLocatorName"));
                                dto.setRcvWhAreaCode((String) map.get("whAreaCode"));
                                dto.setRcvWhAreaCategoryCode((String) map.get("whAreaCategoryCode"));
                            }
                        }
                    }
                }
            }
        }
//		if (!ObjectUtils.isNull(list)) {
//			String[] itemIds = list.stream().map(TaskInstockDetailDTO::getItemId).toArray(String[]::new);
//			// 请求商品信息
//			RestMessage<List<BaseMtlItemMst>> itemResult = itemClient
//					.getListByIds(new HashSet<>(Arrays.asList(itemIds)));
//			for (TaskInstockDetailDTO lossD : list) {
//				if (itemResult.isSuccess() && itemResult.getData() != null) {
//					BaseMtlItemMst itemBaseInfoVO = itemResult.getData().stream()
//							.filter(item -> item.getItemId().equals(lossD.getItemId())).findFirst().orElse(null);
//					if (itemBaseInfoVO != null) {
//						BaseMtlItemWms baseMtlItemHealth = itemBaseInfoVO.getBaseMtlItemWms();
//						if (baseMtlItemHealth != null) {
//							lossD.setItemIsPieceOrCase(baseMtlItemHealth.getIsCaseOrPiece().toString());
//						}
//					}
//				}
//			}
//		}
        Page<TaskInstockDetailDTO> page = PageUtils.getPage(list);
        long end = System.currentTimeMillis();
        logger.info("上架任务查询结束时间: " + end + "(ms)");
        long eb = end - start;
        logger.info("耗时: " + eb + "(ms)");
        return page;
    }

    @Override
    public Page<RcvHDTO> extractRcvHWithNativeSql(InsockTaskRcvQueryVO rcvQueryVO, PageParam pageParam) {
        String enterpriseId = userUtils.getEnterpriseId();
        rcvQueryVO.setEnterpriseId(enterpriseId);
        this.startPage(pageParam);
        List<RcvHDTO> list = this.mapper.extractRcvHWithNativeSql(rcvQueryVO);
//		if (!ObjectUtils.isNull(list)) {
//			List<String> collect = list.stream().map(RcvHDTO :: getRcvHeaderId).collect(Collectors.toList());
//			// 循环获取代价太大
//		}
        return PageUtils.getPage(list);
    }

    @TxTransaction(isStart = true)
    @Transactional
    @Override
    public Object splitTask(List<TaskInstockDetailDTO> taskInstockDetailDTOList) {
        // 注: 拆分后必须有货位 pass
        // 拆分加锁
        UserDTO userDTO = userUtils.getUserDTO();
        boolean editFlag = false;
        int row = 0;
        List<TaskInstockDetail> list = new ArrayList<>();
        List<String> idlist = new ArrayList<>();
        Date now = new Date();
        List<TaskInstockDetail> needRecommend = new ArrayList<>();

        for (TaskInstockDetailDTO taskInstockDetailDTO : taskInstockDetailDTOList) {
            row++;

            BigDecimal pieceQty = taskInstockDetailDTO.getPieceQty();
            if (pieceQty == null || pieceQty.compareTo(BigDecimal.ZERO) == 0) {
                throw new BizException(String.format("第[%d]行,拆分的数量不可为空,或者0", row));
            }

            String taskInboundId = taskInstockDetailDTO.getTaskInboundId();
            if (ObjectUtils.isNotNull(taskInboundId)) {
                editFlag = true;
                // 加入乐观锁
                if (!canCommit(taskInboundId, row, null, taskInstockDetailDTO, true))
                    throw new BizException(
                            String.format("第[%d行],上架行拆分失败!taskInboundId为[%s]的上架任务处理中,请刷新重试!", row, taskInboundId));
            } else {
                editFlag = false;
            }

            String rqcLineId = taskInstockDetailDTO.getRqcLineId();
            RqcLines findById = this.rqcLinesMapper.findById(rqcLineId, RqcLines.class);
            if (findById != null && findById.getPieceQty().compareTo(pieceQty) < 1) {
                throw new BizException(String.format("第[%d]行,拆分的数量不可大于等于验收数量", row));
            }

//			String whLocatorId = taskInstockDetailDTO.getWhLocatorId();
//			AssertUtils.isNotBlank(whLocatorId, "请检查自己的货位是否选择!");
            TaskInstockDetail taskInstockDetail = new TaskInstockDetail();
            BeanUtils.copyProperties(taskInstockDetailDTO, taskInstockDetail);

            this.setBaseSysField(taskInstockDetail, editFlag, userDTO, now);
            this.vaildateModel(taskInstockDetail, editFlag, userDTO);

            // 计算qty,保证准确性
            BigDecimal conversionRate = taskInstockDetail.getConversionRate();
            if (conversionRate == null || BigDecimal.ZERO.compareTo(conversionRate) == 0)
                conversionRate = BigDecimal.ONE;
            BigDecimal divide = pieceQty.divide(conversionRate, 4, RoundingMode.DOWN);
            taskInstockDetail.setQty(divide);
            taskInstockDetail.setStatus("1"); // 可执行状态

            // 上架拆分校验库别可用性
            // 校验库别是否正确,比如散件入了整件库,整件入了散件库
            if (enableVaildateAreaCategory) {
                // 此处应当重新推荐库别
                try {
                    vaildateAreaCategory(taskInstockDetail, row);
                } catch (Exception e) {
                    logger.info("上架任务拆分异常,原因{}", e);
                    needRecommend.add(taskInstockDetail);
                }
            }

            if (ObjectUtils.isNotNull(taskInboundId)) {
                // 更新
                taskInstockDetail.setIsSpilt(1);
                taskInstockDetail.setSpilterName(userDTO.getUsername());
                this.mapper.updateWithValidate(taskInstockDetail,
                        p -> ObjectUtils.isNotNull(p) && ObjectUtils.isNotNull(p.getValue()));
                list.add(taskInstockDetail);
                idlist.add(taskInboundId);
            } else {
                // 新增
                taskInstockDetail.setIsSpilt(0);
                this.mapper.save(taskInstockDetail);
                list.add(taskInstockDetail);
                // 返回前端对应的id
                idlist.add(taskInstockDetail.getTaskInboundId());
            }
        }

        // 重新需要推荐的库别
        if (!ObjectUtils.isNull(needRecommend)) {
            excuteRecommend(needRecommend, userDTO, false);
        }

        List<TaskInstockDetailDTO> collect = Optional.ofNullable(list).orElse(Collections.emptyList()).stream()
                .map(p -> {
                    TaskInstockDetailDTO target = new TaskInstockDetailDTO();
                    BeanUtils.copyProperties(p, target);
                    return target;
                }).collect(Collectors.toList());
        // TODO 预占库位远程操作
        try {
            excutePreInStock(collect, userDTO); // 调用报错,待处理
        } catch (Exception e) {
            e.printStackTrace();
        }
        return idlist;
    }

    @TxTransaction(isStart = true)
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Object confirmSplitTask(TaskInstockDetailDTO taskInstockDetailDTO) {
        String warehouseId = taskInstockDetailDTO.getWarehouseId();
        String enterpriseId = taskInstockDetailDTO.getEnterpriseId();
        if (taskInstockDetailDTO.getPieceQty() == null || taskInstockDetailDTO.getPieceQty().compareTo(BigDecimal.ZERO) == 0) {
            throw new BizException("拆分的数量不可为空,或者0");
        }
        double pieceQty = taskInstockDetailDTO.getPieceQty().doubleValue();//商品数量
        double newPieceQty = taskInstockDetailDTO.getNewPieceQty().doubleValue(); //上架数量
        double newCaseQty = taskInstockDetailDTO.getNewCaseQty().doubleValue(); //件数
        double newOddPieceQty = taskInstockDetailDTO.getNewOddPieceQty().doubleValue(); //零数
        if (warehouseId == null || warehouseId == "") {
            throw new BizException("仓不能为空！");
        }
        if (enterpriseId == null || enterpriseId == "") {
            throw new BizException("企业不能为空！");
        }

        //拆分的容器
        String containerFrom = taskInstockDetailDTO.getContainerFrom();
        Page<BaseWmsWhContainer> containerPage = commonRemoteService.getContainerPage(containerFrom, null, warehouseId, null, null);
        List<BaseWmsWhContainer> list = containerPage.getList();
        if (list.isEmpty()) {
            throw new BizException(String.format("容器号[%s]不存在！", containerFrom));
        }
        if (!list.get(0).getContainerStatus().equals("10")) {
            throw new BizException(String.format("容器号[%s]不是空闲状态！", containerFrom));
        }
        if (newPieceQty > pieceQty) {
            throw new BizException("拆分数量不能大于任务数量");
        }

        UserDTO userDTO = userUtils.getUserDTO();
        String taskInboundId = taskInstockDetailDTO.getTaskInboundId();
        // 加入乐观锁
        if (!splitTaskCommit(taskInboundId, 1, null, taskInstockDetailDTO, true))
            throw new BizException(String.format("上架行拆分失败!taskInboundId为[%s]的上架任务处理中,请刷新重试!", taskInboundId));
        TaskInstockDetail taskInstockDetail = new TaskInstockDetail();
        BeanUtils.copyProperties(taskInstockDetailDTO, taskInstockDetail);
        this.setBaseSysField(taskInstockDetail, false, userDTO, new Date());

        if (newPieceQty == pieceQty) {  //拆分数量等于任务数量
            taskInstockDetail.setContainerCode(containerFrom);
            taskInstockDetail.setIsSpilt(1);
            taskInstockDetail.setSpilterName(userDTO.getUsername());
            this.mapper.updateWithValidate(taskInstockDetail,
                    p -> ObjectUtils.isNotNull(p) && ObjectUtils.isNotNull(p.getValue()));

            ContainerQueryVO vo1 = new ContainerQueryVO();
            vo1.setEnterpriseId(enterpriseId);
            vo1.setWarehouseId(warehouseId);
            vo1.setContainerCode(taskInstockDetailDTO.getContainerCode());
            baseWmsWarehouseCaller.release(vo1);  // 更新原容器的状态为空闲
        }
        if (newPieceQty < pieceQty) {  //拆分数量小于任务数量
            //新增拆分任务
            taskInstockDetail.setIsSpilt(0);
            taskInstockDetail.setPieceQty(new BigDecimal(newPieceQty));
            taskInstockDetail.setContainerCode(containerFrom);
            this.mapper.save(taskInstockDetail);

            //更新原任务
            TaskInstockDetail byId = taskInstockDetailMapper.findById(taskInstockDetailDTO.getTaskInboundId(), TaskInstockDetail.class);
            byId.setPieceQty(new BigDecimal(pieceQty - newPieceQty));
            byId.setIsSpilt(1);
            this.mapper.update(byId);

        }
        ContainerQueryVO vo = new ContainerQueryVO();
        vo.setEnterpriseId(enterpriseId);
        vo.setWarehouseId(warehouseId);
        vo.setContainerCode(containerFrom);
        baseWmsWarehouseCaller.used(vo);  //更新目标容器为占用状态

        return "拆分成功";
    }

    private boolean splitTaskCommit(String taskId, int row, String statusResult, TaskInstockDetailDTO taskInstockDetailDTO,
                                    boolean splitFlag) {
        // 数据库乐观锁
        TaskInstockDetail taskInstockDetail = this.mapper.findById(taskId, TaskInstockDetail.class);
        AssertUtils.notNull(taskInstockDetail, String.format("第[%d]行,上架任务id:[%s]查询的任务为空!", row, taskId));
        String status = taskInstockDetail.getStatus();
        if (!"1".equals(status))
            throw new BizException(String.format("第[%d]行,上架任务id:[%s]查询的任务已经执行完毕,请刷新重试!", row, taskId));

        Criteria criteria = new Criteria(TaskInstockDetail.class);
        Integer dataVersion = taskInstockDetail.getDataVersion();

        // 这里只需要dataVersion其实就可以了
        criteria.setRestriction(Restrictions.and(Restrictions.eq("taskInboundId", taskId),
                Restrictions.eq("dataVersion", dataVersion)));
        Map<String, Object> map = MapUtils.put("dataVersion", dataVersion + 1).getMap();
        if (!StringUtils.isBlank(statusResult)) {
            map.put("status", "0");
        }
        long count = this.mapper.updateMapByCriteria(map, criteria);
        if (0 >= count)
            return false;
        return true;
    }

    private void vaildateAreaCategory(TaskInstockDetail line, int row) {
        String whAreaCategory = line.getWhAreaCategoryCode();
        if (!StringUtils.isBlank(whAreaCategory)) {
            String caseOrPiece = this.getCaseOrPiece(line);
            if ("0".equals(caseOrPiece) && BizConstant.LHK.equals(line)) {
                throw new BizException(String.format("第[%d]行,整件选择了零货库,请检查!", row));
            }
            if ("1".equals(caseOrPiece) && BizConstant.ZJK.equals(whAreaCategory)) {
                throw new BizException(String.format("第[%d]行,零散选择了整件库,请检查!", row));
            }
        }

    }

    private String getCaseOrPiece(TaskInstockDetail target) {
        // 是否整散
        BigDecimal pieceQty = target.getPieceQty();
        BigDecimal conversionRate = target.getConversionRate();
        BigDecimal oddCaseQty = pieceQty.divide(conversionRate, 0, RoundingMode.DOWN);
        BigDecimal oddPieceQty = pieceQty.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";
    }

    @Transactional
    @TxTransaction(isStart = true)
    @Override
    public Object taskMerge(List<TaskInstockDetailDTO> taskInstockDetailDTOList) {
        int size = taskInstockDetailDTOList.size();
        if (size < 2) {
            throw new BizException("至少需要两个及以上的任务才可做合并操作!");
        }
        // 任务合并
        UserDTO userDTO = userUtils.getUserDTO();
        boolean editFlag = false;
        int row = 1;
        Date now = new Date();
        // 合并后是否需要记录合并后的id,或者合并前的任务id集合
        TaskInstockDetailDTO detailDTO = taskInstockDetailDTOList.get(0); // 以以第一行为基准

        List<TaskInstockDetail> needRecommend = new ArrayList<>();
        // Step1: 是否可合并校验
        for (int i = 1; i < size; i++) {
            row++;
            TaskInstockDetailDTO taskInstockDetailDTO = taskInstockDetailDTOList.get(i);
            boolean b = equal(taskInstockDetailDTO, detailDTO);
            if (!b) {
                throw new BizException(String.format("第[%d]行,不是同品同批的商品,无法进行合并操作,请检查重试!", row));
            }
        }
        int row2 = 0;
        BigDecimal mergePieceQty = BigDecimal.ZERO;
        List<String> taskIds = new ArrayList<>();
        for (TaskInstockDetailDTO taskDTO : taskInstockDetailDTOList) {
            row2++;
            String taskId = taskDTO.getTaskInboundId();
            editFlag = true;
            // 加入乐观锁
            if (!canCommit(taskId, row, "0", taskDTO, false))
                throw new BizException(String.format("第[%d行],任务合并失败!任务id为[%s]的任务处理中,请刷新重试!", row2, taskId));
            BigDecimal pieceQty = taskDTO.getPieceQty();
            mergePieceQty.add(pieceQty);
            taskIds.add(taskId);
        }
        if (!ObjectUtils.isNull(taskIds)) {
            // 取消预占
            InvStockCancel cancel = new InvStockCancel();
            cancel.setLineBillIds(taskIds.toArray(new String[taskIds.size()]));
            RestMessage inCancel = null;
            try {
                inCancel = baseInventoryRemoteCaller.inCancel(cancel);
            } catch (Exception e) {
                logger.error("上架任务合并操作,取消预占失败->原因{}", e);
                throw new BizException(String.format("上架任务合并操作,取消预占失败->原因[%s]", e.getMessage()));
            }
            if (inCancel != null && !inCancel.isSuccess()) {
                throw new BizException(String.format("上架任务合并操作,取消预占失败->原因[%s]", inCancel.getData().toString()));
            }
        }

        // 重新生成新的任务
        // 计算整件,零散
        TaskInstockDetail detail = new TaskInstockDetail();
        BeanUtils.copyProperties(detailDTO, detail);
        this.buildField(detail, false, userDTO, now);

        detail.setActiveFlag("0"); // 默认允许作业
        detail.setStatus("1");
        BigDecimal conversionRate = detail.getConversionRate();
        if (conversionRate == null) {
            conversionRate = BigDecimal.ONE;
        }
        detail.setConversionRate(conversionRate);
        detail.setQty(mergePieceQty.divide(conversionRate, 2, RoundingMode.DOWN));
        detail.setTaskInboundId(String.valueOf(SnowFlakeUtils.getId()));
        this.mapper.save(detail);

        // 校验库别是否正确,比如散件入了整件库,整件入了散件库
        if (enableVaildateAreaCategory) {
            // 此处应当重新推荐库别
            try {
                vaildateAreaCategory(detail, row);
            } catch (Exception e) {
                logger.info("上架任务拆分异常,原因{}", e);
                needRecommend.add(detail);
            }
        }
        // 重新需要推荐的库别
        if (!ObjectUtils.isNull(needRecommend)) {
            excuteRecommend(needRecommend, userDTO, false);
        }

        String whLocatorId = detail.getWhLocatorId();
        if (!StringUtils.isBlank(whLocatorId)) {
            // 生成预占
            ValidList<InvStockIn> invStockList = new ValidList<>();
            InvStockIn stockIn = new InvStockIn();
            stockIn.setAsnId(detail.getAsnHeaderId());
            stockIn.setAsnLineId(detail.getAsnLineId());
            stockIn.setEnterpriseId(detail.getEnterpriseId());
            stockIn.setRcvId(detail.getRcvHeaderId());
            stockIn.setRcvLineId(detail.getRcvLineId());
            stockIn.setTaskId(detail.getTaskInboundId());
            stockIn.setWarehouseId(detail.getWarehouseId());
            List<InvStockInDetail> datails = new ArrayList<>();
            InvStockInDetail datail = new InvStockInDetail();
            datail.setBatchId(detail.getBatchId());
            datail.setComments(detail.getComments());
            datail.setContainerCode(detail.getContainerCode());
            datail.setGoodsCaseCode(detail.getGoodsCaseCode());
            datail.setLocatorId(whLocatorId);
            datail.setQuantity(detail.getPieceQty());
            datail.setStatus(detail.getInventoryStatus());
            // 二级库存状态
            String secondStatus = detail.getSecondStatus();
            if (!StringUtils.isBlank(secondStatus)) {
                datail.setStatus(secondStatus);
            }

            datails.add(datail);
            stockIn.setDetails(datails);
            try {
                baseInventoryRemoteCaller.inStock(invStockList);
            } catch (Exception e) {
                logger.error("上架任务合并操作,预占失败->原因{}", e);
                throw new BizException(String.format("上架任务合并操作,预占失败->原因[%s]", e.getMessage()));
            }
        }

        return true;
    }

    private boolean equal(TaskInstockDetailDTO taskInstockDetailDTO, TaskInstockDetailDTO detailDTO) {
        String itemId = detailDTO.getItemId();
        String batchId = detailDTO.getBatchId();
        int hashCode = itemId.concat(batchId).hashCode();

        String itemId2 = taskInstockDetailDTO.getItemId();
        String batchId2 = taskInstockDetailDTO.getBatchId();
        int hashCode2 = itemId2.concat(batchId2).hashCode();
        if (hashCode == hashCode2) {
            return true;
        }
        return false;
    }

    @Override
    public List<TaskInstockDetailDTO> geTaskInstockDetailsByRcvLineId(List<String> rcvLineIdList) {
        return mapper.geTaskInstockDetailsByRcvLineId(rcvLineIdList);
    }

//	/**
//	 * 远程调用库存服务调整库存
//	 *
//	 * @param collect
//	 * @param userDTO
//	 */
//	private void excutePreInStock(List<TaskInstockDetailDTO> collect, UserDTO userDTO) {
//		ValidList<InvStockIn> invStock = new ValidList<>();
//		Map<String, List<TaskInstockDetailDTO>> collect2 = Optional.ofNullable(collect).orElse(Collections.emptyList())
//				.stream().distinct().collect(Collectors.groupingBy(TaskInstockDetailDTO::getItemId));
//
//		for (List<TaskInstockDetailDTO> infoList2 : collect2.values()) {
//			if (ObjectUtils.isNull(infoList2))
//				continue;
//			InvStockIn invStockIn = new InvStockIn();
//			List<InvStockInDetail> details = new ArrayList<InvStockInDetail>();
//			TaskInstockDetailDTO taskInstockDetailDTO = infoList2.get(0);
//			for (TaskInstockDetailDTO info : infoList2) {
//				InvStockInDetail detail = new InvStockInDetail();
//				BigDecimal pieceQty = info.getPieceQty();
//				String warehouseLocatorId = info.getWhLocatorId();
//				String comments = info.getComments();
//				String goodsCaseCode = info.getGoodsCaseCode();
//				String containerCode = info.getContainerCode();
//
////				// 这里需要改变,先用结论顶替 TODO
////				String result = info.getResult();
////				// 库存状态
////				switch (result) {
////				case "0":
////					detail.setStatus(StockStatusEnum.QUALIFIED.getCode());
////					break;
////				case "1":
////					detail.setStatus(StockStatusEnum.UNQUALIFIED.getCode());
////					break;
////				case "2":
////					detail.setStatus(StockStatusEnum.WAITING_INSPECTION.getCode());
////					break;
////				case "3":
////					detail.setStatus(StockStatusEnum.REFUND.getCode());
////					break;
////				default:
////					break;
////				}
//				String inventoryStatus = info.getInventoryStatus();
//				// 库存状态
//				detail.setStatus(inventoryStatus);
//
//				detail.setQuantity(pieceQty);
//				detail.setContainerCode(containerCode);
//				detail.setGoodsCaseCode(goodsCaseCode);
//				detail.setLocatorId(warehouseLocatorId);
//				detail.setComments(comments);
//				details.add(detail);
//			}
//
//			// 远程库存服务需修改 taskId
//			invStockIn.setDetails(details);
//			BeanUtils.copyProperties(taskInstockDetailDTO, invStockIn);
//			invStockIn.setAsnId(taskInstockDetailDTO.getAsnHeaderId());
//			invStockIn.setCargoOwnerId(taskInstockDetailDTO.getMtlOwnerId());
//			invStockIn.setEnterpriseId(userDTO.getEnterpriseId());
//			invStockIn.setRcvId(taskInstockDetailDTO.getRcvHeaderId());
//			invStockIn.setRcvLineId(taskInstockDetailDTO.getRcvLineId());
//			invStockIn.setTaskId(taskInstockDetailDTO.getTaskInboundId());
//			invStock.add(invStockIn);
//		}
//		baseInventoryRemoteCaller.adjustPreStock(invStock);
//	}
}
