package com.prolog.cs.biz.outbound.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.codingapi.tx.annotation.TxTransaction;
import com.prolog.common.RestMsgHelper;
import com.prolog.cs.basic.common.model.BaseCommonDict;
import com.prolog.cs.basic.item.model.*;
import com.prolog.cs.basic.item.model.vo.ItemBarCodeVO;
import com.prolog.cs.basic.item.model.vo.WaveItemInfoVO;
import com.prolog.cs.basic.warehouse.model.BaseWmsWarehouseArea;
import com.prolog.cs.basic.warehouse.model.BaseWmsWarehouseMst;
import com.prolog.cs.basic.warehouse.model.BaseWmsWhWorkstation;
import com.prolog.cs.basic.warehouse.model.WmsWhSlideway;
import com.prolog.cs.basic.warehouse.model.dto.WmsWhSlidewayDTO;
import com.prolog.cs.basic.warehouse.model.vo.ContainerQueryVO;
import com.prolog.cs.basic.warehouse.model.vo.WhWorkstationQueryVO;
import com.prolog.cs.biz.inbound.dto.IsSuperviseDTO;
import com.prolog.cs.biz.outbound.dao.*;
import com.prolog.cs.biz.outbound.funcflow.innnercheckfunc.*;
import com.prolog.cs.biz.outbound.model.*;
import com.prolog.cs.biz.outbound.model.dto.check.*;
import com.prolog.cs.biz.outbound.model.entity.*;
import com.prolog.cs.biz.outbound.model.vo.WorkgroupVO;
import com.prolog.cs.biz.outbound.model.vo.check.*;
import com.prolog.cs.biz.outbound.resource.*;
import com.prolog.cs.biz.outbound.service.InnerCheckV2Service;
import com.prolog.cs.biz.outbound.service.strategy.STRATEGY_CODE;
import com.prolog.cs.biz.outbound.service.strategy.StrategySelector;
import com.prolog.cs.biz.outbound.service.strategy.handles.IStrategyHandle;
import com.prolog.cs.biz.outbound.utils.BillUtils;
import com.prolog.cs.data.exchange.client.ExchangeSender;
import com.prolog.entity.vo.ApplyForPickSlideVO;
import com.prolog.framework.authority.core.dto.UserDTO;
import com.prolog.framework.authority.user.UserTemplate;
import com.prolog.framework.bz.common.print.PrintData;
import com.prolog.framework.bz.common.print.ReportData;
import com.prolog.framework.bz.core.collection.ValidList;
import com.prolog.framework.bz.core.model.ModelUtils;
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.exception.PrologException;
import com.prolog.framework.core.pojo.Page;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Restriction;
import com.prolog.framework.core.restriction.Restrictions;
import com.prolog.framework.lock.DistributedLock;
import com.prolog.framework.toolkit.MapToolKit;
import com.prolog.framework.utils.AssertUtils;
import com.prolog.framework.utils.MapUtils;
import com.prolog.framework.utils.SnowFlakeUtils;
import com.prolog.framework.utils.StringUtils;
import com.prolog.function.annotation.Flow;
import com.prolog.function.annotation.FuncImpl;
import com.prolog.upcloud.base.inventory.vo.InvStockOut;
import com.prolog.upcloud.base.inventory.vo.InvStockOutDetail;
import com.prolog.upcloud.base.strategy.model.BusinessControl;
import com.prolog.upcloud.base.wh.operate.vo.WmsAdjustAddVO;
import org.apache.commons.compress.utils.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicInteger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class InnerCheckV2ServiceImpl implements InnerCheckV2Service {

    private static Logger log = LoggerFactory.getLogger(InnerCheckV2ServiceImpl.class);

    @Autowired
    private OperateResource operateResource;

    @Autowired
    private WaveStorageMapper waveStorageMapper;

    @Autowired
    private MoveTaskMapper moveTaskMapper;

    @Autowired
    private ChainResource chainResource;

    @Autowired
    private WorkStationUserMapper workStationUserMapper;

    @Autowired
    private ControlResource controlResource;

    @Autowired
    PickTaskHMapper pickTaskHMapper;

    @Autowired
    PickTaskDMapper pickTaskDMapper;

    @Autowired
    UserTemplate userTemplate;

    @Autowired
    WaveBillDMapper billDMapper;

    @Autowired
    ItemClient itemClient;

    @Autowired
    BarcodeHMapper barcodeHMapper;

    @Autowired
    BarcodeDMapper barcodeDMapper;

    @Autowired
    private WaveBillMapper billMapper;

    @Autowired
    private InnerCheckHMapper innerCheckHMapper;

    @Autowired
    WorkStageRuntimeMapper workStageRuntimeMapper;

    @Autowired
    private InnerCheckDMapper innerCheckDMapper;

    @Autowired
    private WaveStorageMapper storageMapper;

    @Autowired
    private OmsDoBillHMapper omsDoBillHMapper;

    @Autowired
    private OmsDoBillDMapper omsDoBillDMapper;

    @Autowired
    private WarehouseResource warehouseResource;

    @Autowired
    private InventoryResource inventoryResource;

    @Autowired
    private InBoundClient inBoundClient;

    @Autowired
    private DistributedLock distributedLock;

    @Autowired
    private SaleRedRecordMapper saleRedRecordMapper;

    @Autowired
    private ExchangeSender exchangeSender;

    @Autowired
    private CommonClient commonClient;

    @Autowired
    private StrategyResource strategyResource;

    @Value("${prolog.dao.keyGenerator.snowFlake.dataCenterId:15}")
    private long dataCenterId;

    @Value("${prolog.dao.keyGenerator.snowFlake.machineId:2}")
    private long machineId;

    // 业务参数配置内/外复核回传ERP by Huanglh 20220112
    @Autowired
    private StrategySelector strategySelector;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private PickOrderMapper pickOrderMapper;

    private ValidPrintBoxFunc validPrintBoxFunc;
    private PrintLclFunc printLclFunc;
    private ValidInnerCheckConfirmFunc validInnerCheckConfirmFunc;
    private InnerCheckConfirmFunc innerCheckConfirmFunc;

    @PostConstruct
    public void init() {
        validPrintBoxFunc = this::validPrintLclFunc;
        printLclFunc = this::printLclFunc;
        validInnerCheckConfirmFunc = this::validInnerCheckConfirmFunc;
        innerCheckConfirmFunc = this::innerCheckConfirmFunc;
    }

    @Override
    @Transactional
    @Flow(name = "内复核索取", category = "出库/复核管理/内复核/内复核索取", index = 1, description = "1、查询容器内任务明细 2、更新状态为已索取：pick_task_h status=3,pick_task_d flow_status=4")
    public RestMessage<InnerCheckBillV2VO> getCurrentContainer(
            String containerCode,
            String stationCode,
            String billGroupId,
            Integer lclNo,
            Integer isDoubleCheck,
            String warehouseId
    ) {
        long start = System.currentTimeMillis();
        InnerCheckBillV2VO containerInfo = findPickupByContainerFuncV2(containerCode, stationCode, null, billGroupId, lclNo, isDoubleCheck, warehouseId);
        long end = System.currentTimeMillis();
        log.info("getCurrentContainer接口耗时：" + (end - start));
        return RestMessage.doSuccess(containerInfo);
    }

    public InnerCheckBillV2VO findPickupByContainerFunc(
            String containerCode,
            String stationCode,
            String pickTaskDId,
            String billGroupId,
            Integer lclNo,
            Integer isDoubleCheck
    ) {
        AssertUtils.isNotBlank(stationCode, "入参复核台不能为空");
        AssertUtils.isNotBlank(containerCode, "入参容器号不能为空");
        Criteria crt = Criteria.forClass(PickTaskH.class); // 查询未复核完成的容器
        Restriction r1 = Restrictions.in("status", new String[]{"0", "1", "2", "3"});
        Restriction r2 = Restrictions.eq("containerCode", containerCode);
        Restriction r3 = Restrictions.or(Restrictions.isnull("stationCode"), Restrictions.in("stationCode", new String[]{"-1", stationCode})
        );
        // 使用or实现，业务设计，只能查出一个pick_task_h
        crt.setRestriction(Restrictions.and(r1, r2, r3));

        List<PickTaskH> pckTaskHs = pickTaskHMapper.findByCriteria(crt);
        if (pckTaskHs.isEmpty()) {
            throw new BizException("找不到对应容器任务");
        } else if (pckTaskHs.size() > 1) {
            throw new BizException("容器绑定错误，该容器绑定了多个未完成任务");
        }
        PickTaskH pckTaskH = pckTaskHs.get(0);
        String pickTaskHId = pckTaskH.getPickTaskId();
        String stationCodeDb = pckTaskH.getStationCode();
        // 分支代码，本次不优化
        if (StringUtils.isBlank(stationCodeDb) || "-1".equals(stationCodeDb)) {
            // 机动复核台逻辑
            WhWorkstationQueryVO workstationQueryVO = new WhWorkstationQueryVO();
            workstationQueryVO.setWarehouseId(pckTaskH.getWarehouseId());
            workstationQueryVO.setCode(stationCode); // 条件没生效，后续优化
            RestMessage<List<BaseWmsWhWorkstation>> rest = warehouseResource.queryList(workstationQueryVO);
            MapToolKit tempStation = MapUtils.put("stationCode", stationCode);
            if (rest.isSuccess() && rest.getData() != null) {
                BaseWmsWhWorkstation vo = rest.getData().stream().filter(t -> t.getStationCode().equals(stationCode)).findFirst().orElse(null);
                if (vo != null) {
                    tempStation.put("stationId", vo.getStationId());
                    tempStation.put("stationName", vo.getStationName());
                }
            }
            pickTaskHMapper.updateMapById(pickTaskHId, tempStation.getMap(), PickTaskH.class);
        }
        // 这里逻辑改为默认不展示拼箱，拼箱数据由后面的逻辑查询
        // 河南省医药的一个拼箱可以对应多个周转箱，因此可能出现一个周转箱中的物料部分已装入已封箱的拼箱
        List<InnerCheckTaskDV2VO> dBInnerCheckTaskDVOs = pickTaskDMapper.getPickTaskdV2(pickTaskHId, false);
        Criteria redCrt = Criteria.forClass(SaleRedRecord.class);
        redCrt.restrictionBuilder().in("pickTaskDId", dBInnerCheckTaskDVOs.stream().map(InnerCheckTaskDV2VO::getPickTaskDId).distinct().toArray());
        redCrt.buildRestriction();
        List<SaleRedRecord> saleRedRecords = saleRedRecordMapper.findByCriteria(redCrt);
        dBInnerCheckTaskDVOs = dBInnerCheckTaskDVOs.stream().filter(x -> x.getPickQty() != null).collect(Collectors.toList());
        String redisKey = "wms:innerCheck:" + pickTaskHId;
        Map redisMap = (Map) redisTemplate.opsForValue().get(redisKey);
        InnerCheckBillV2VO innerCheckBillVO = new InnerCheckBillV2VO();
        innerCheckBillVO.setStationCode(stationCode);
        List<BaseMtlItemMst> itemList = null;
        if (redisMap != null) {
            innerCheckBillVO = JSON.parseObject(redisMap.get("vo").toString(), InnerCheckBillV2VO.class);
            itemList = JSON.parseArray(redisMap.get("items").toString(), BaseMtlItemMst.class);
            for (InnerCheckTaskDV2VO dBInnerCheckTaskDV2VO : dBInnerCheckTaskDVOs) {
                String tempPickTaskDId = dBInnerCheckTaskDV2VO.getPickTaskDId();
                InnerCheckTaskDV2VO redisInnerCheckTaskDV2VO = innerCheckBillVO.getInnerCheckTaskDVOS()
                        .stream()
                        .filter(t -> t.getPickTaskDId().equals(tempPickTaskDId))
                        .findFirst()
                        .orElse(null);
                if (redisInnerCheckTaskDV2VO != null) {
                    dBInnerCheckTaskDV2VO.setIsSupervise(redisInnerCheckTaskDV2VO.getIsSupervise());
                    // dBInnerCheckTaskDV2VO.setNoSuperviseReason(redisInnerCheckTaskDV2VO.getNoSuperviseReason());
                    dBInnerCheckTaskDV2VO.setSalespersonName(redisInnerCheckTaskDV2VO.getSalespersonName());
                    dBInnerCheckTaskDV2VO.setPaymentType(redisInnerCheckTaskDV2VO.getPaymentType());
                    dBInnerCheckTaskDV2VO.setCreateby(redisInnerCheckTaskDV2VO.getCreateby());
                    dBInnerCheckTaskDV2VO.setRemark(redisInnerCheckTaskDV2VO.getRemark());
                    dBInnerCheckTaskDV2VO.setShipperId(redisInnerCheckTaskDV2VO.getShipperId());
                    dBInnerCheckTaskDV2VO.setConsignorId(redisInnerCheckTaskDV2VO.getConsignorId());
                    dBInnerCheckTaskDV2VO.setBillCode(redisInnerCheckTaskDV2VO.getBillCode());
                    dBInnerCheckTaskDV2VO.setBillType(redisInnerCheckTaskDV2VO.getBillType());
                    dBInnerCheckTaskDV2VO.setCargoOwnerName(redisInnerCheckTaskDV2VO.getCargoOwnerName());
                }
                extractedItem(itemList, dBInnerCheckTaskDV2VO);
            }
        } else {
            innerCheckBillVO.setWarehouseId(pckTaskH.getWarehouseId());
            innerCheckBillVO.setPickTaskHId(pickTaskHId);
            innerCheckBillVO.setBillGroupId(pckTaskH.getBillGroupId());
            innerCheckBillVO.setContainerCode(containerCode);
            Criteria cr = Criteria.forClass(WaveBillDO.class);
            Restriction restriction = Restrictions.in("id", dBInnerCheckTaskDVOs.stream().map(InnerCheckTaskDV2VO::getBillId).distinct().collect(Collectors.toList()));
            cr.setRestriction(restriction);
            List<WaveBillDO> billHList = billMapper.findByCriteria(cr);
            if (billHList != null && billHList.size() > 0) {
                WaveBillDO waveBillDO = billHList.get(0);
                innerCheckBillVO.setShipperId(waveBillDO.getShipperId());
                innerCheckBillVO.setShipperName(waveBillDO.getShipperName());
                innerCheckBillVO.setConsignorId(waveBillDO.getConsignorId());
                innerCheckBillVO.setConsignorName(waveBillDO.getConsignorName());
                innerCheckBillVO.setBillCode(waveBillDO.getSrcCode());
                innerCheckBillVO.setBillType(waveBillDO.getType());
            }

            // 此sql优化，次优先级
            // 根据id查询商品批次信息，用来获取有效期
            Set<String> itemIdSet = dBInnerCheckTaskDVOs.stream().map(InnerCheckTaskDV2VO::getItemId).collect(Collectors.toSet());
            if (itemIdSet.size() > 0 && !StringUtils.isBlank(itemIdSet.iterator().next())) {
                itemList = itemClient.getListByIds(itemIdSet).getData();
            }
            for (InnerCheckTaskDV2VO innerCheckTaskDVO : dBInnerCheckTaskDVOs) {
                if (billHList != null && billHList.size() > 0) {
                    WaveBillDO bill = billHList.stream().filter(t -> t.getId().equals(innerCheckTaskDVO.getBillId())).findFirst().orElse(null);
                    if (bill != null) {
                        innerCheckTaskDVO.setSalespersonName(bill.getSalesPersonName());
                        innerCheckTaskDVO.setPaymentType(bill.getPaymentDayType());
                        innerCheckTaskDVO.setCreateby(bill.getCreateBy());
                        innerCheckTaskDVO.setRemark(bill.getRemark());
                        innerCheckTaskDVO.setShipperId(bill.getShipperId());
                        innerCheckTaskDVO.setConsignorId(bill.getConsignorId());
                        innerCheckTaskDVO.setCargoOwnerCode(bill.getShipperCode());
                        innerCheckTaskDVO.setBillCode(bill.getSrcCode());
                        innerCheckTaskDVO.setBillType(bill.getType());
                        innerCheckTaskDVO.setCargoOwnerName(bill.getConsignorName());
                    }
                }
                extractedItem(itemList, innerCheckTaskDVO);
            }
            // 扫码监管码必须要传参数：货主+客户+商品，顺序必须在后
            // 阶段：0=入库质检 1=PC拣货 2=PC内复核 3=PC外复核 4=补录 5=PDA拣货 6=PDA内复核 7=PDA外复核
            if (dBInnerCheckTaskDVOs.size() > 0) {
                try {
                    RestMessage<List<? extends IsSuperviseDTO>> rest = inBoundClient.isSupervise(dBInnerCheckTaskDVOs, 2);
                    if (rest.isSuccess() && rest.getData() != null && rest.getData().size() == dBInnerCheckTaskDVOs.size()) {
                        for (int i = 0; i < rest.getData().size(); i++) {
                            BeanUtils.copyProperties(rest.getData().get(i), dBInnerCheckTaskDVOs.get(i));
                        }
                    }
                } catch (Exception e) {
                    log.error("调用监管码接口异常", e);
                }
            }
        }
        innerCheckBillVO.setInnerCheckTaskDVOS(dBInnerCheckTaskDVOs);
        if (pickTaskDId != null) {
            innerCheckBillVO.setCurrentCheckId(pickTaskDId);
        } else {
            if (!"3".equals(pckTaskH.getStatus())) {
                pickTaskHMapper.updateMapById(pickTaskHId, MapUtils.put("status", "3").getMap(), PickTaskH.class);
                updateFlowStatus(pickTaskHId, 4);
            }
        }

        InnerCheckContainerVO containerVO = new InnerCheckContainerVO();
        Set<String> totalItemSet = new HashSet<>();
        Set<String> checkedItemSet = new HashSet<>();
        Set<String> totalLotNoSet = new HashSet<>();
        Set<String> checkedLotNoSet = new HashSet<>();
        BigDecimal redNum = BigDecimal.ZERO;
        BigDecimal totalNum = BigDecimal.ZERO;
        BigDecimal checkedNum = BigDecimal.ZERO;
        Set<String> set = new HashSet<>();
        for (InnerCheckTaskDV2VO innerCheckTaskDVO : dBInnerCheckTaskDVOs) {
            List<SaleRedRecord> subRedList = saleRedRecords.stream().filter(t -> t.getPickTaskDId().equals(innerCheckTaskDVO.getPickTaskDId())).collect(Collectors.toList());
            innerCheckTaskDVO.setRedQty(subRedList.stream().map(SaleRedRecord::getQty).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
            innerCheckTaskDVO.setRedReason(subRedList.stream().map(SaleRedRecord::getReason).filter(reason -> !StringUtils.isBlank(reason)).collect(Collectors.joining("|")));
            // 计算容器信息
            // 1.总品种
            totalItemSet.add(innerCheckTaskDVO.getItemId());
            // 2.已经检查的品种数量
            if (isChecked(innerCheckTaskDVO)) {
                checkedItemSet.add(innerCheckTaskDVO.getItemId());
                if (!StringUtils.isBlank(innerCheckTaskDVO.getLotNo())) {
                    checkedLotNoSet.add(innerCheckTaskDVO.getLotNo());
                }
            }
            // 4.合计批号
            if (!StringUtils.isBlank(innerCheckTaskDVO.getLotNo())) {
                totalLotNoSet.add(innerCheckTaskDVO.getLotNo());
            }
            // 5.订单红冲数量
            if (innerCheckTaskDVO.getRedQty() != null) {
                redNum = redNum.add(innerCheckTaskDVO.getRedQty());
            }

            if (!set.contains(innerCheckTaskDVO.getPickTaskDId())) {
                BigDecimal pickQty = innerCheckTaskDVO.getPickQty();
                totalNum = totalNum.add(pickQty == null ? BigDecimal.ZERO : pickQty);
                if (isChecked(innerCheckTaskDVO)) {
                    if (StringUtils.isBlank(innerCheckTaskDVO.getChecker2())) {
                        checkedNum = checkedNum.add(innerCheckTaskDVO.getCheckQty() == null ? BigDecimal.ZERO : innerCheckTaskDVO.getCheckQty());
                    } else {
                        checkedNum = checkedNum.add(innerCheckTaskDVO.getCheck2Qty() == null ? BigDecimal.ZERO : innerCheckTaskDVO.getCheck2Qty());
                    }
                }
                set.add(innerCheckTaskDVO.getPickTaskDId());
            }
        }
        innerCheckBillVO.setInnerCheckTaskDVOS(dBInnerCheckTaskDVOs);
        containerVO.setTotalVarieties(totalItemSet.size());
        containerVO.setCheckedVarieties(checkedItemSet.size());
        containerVO.setUnCheckedVarieties(containerVO.getTotalVarieties() - containerVO.getCheckedVarieties());
        containerVO.setTotalLot(totalLotNoSet.size());
        containerVO.setCheckedLot(checkedLotNoSet.size());
        containerVO.setUnCheckedLot(containerVO.getTotalLot() - containerVO.getCheckedLot());
        containerVO.setRedNum(redNum);
        containerVO.setTotalNum(totalNum);
        containerVO.setCheckedNum(checkedNum);
        BigDecimal unCheckedNum = totalNum.subtract(checkedNum).subtract(redNum);
        containerVO.setUnCheckedNum(unCheckedNum);
        innerCheckBillVO.setContainerInfo(containerVO);

        // 查询未复核完成的容器
        Criteria crt1 = Criteria.forClass(PickTaskH.class);
        Restriction r11 = Restrictions.in("status", new String[]{"0", "1"});
        Restriction r21 = Restrictions.eq("billGroupId", innerCheckBillVO.getBillGroupId());
        crt1.setRestriction(Restrictions.and(r11, r21));
        containerVO.setOrderUncheckedTaskNum(pickTaskHMapper.findCountByCriteria(crt1));
        //查询拼箱信息
        innerCheckBillVO.setLclVO(queryLcl(stationCode, pckTaskH.getBillGroupId(), lclNo, containerCode, isDoubleCheck, itemList));
        // 循环设置lvlNo和lvlNum
        for (InnerCheckTaskDV2VO innerCheckTaskDVO : innerCheckBillVO.getInnerCheckTaskDVOS()) {
            if (null != innerCheckBillVO.getLclVO() && CollUtil.isNotEmpty(innerCheckBillVO.getLclVO().getLcls())) {
                for (BarCodeHVO barCodeHVO : innerCheckBillVO.getLclVO().getLcls()) {
                    if (CollUtil.isNotEmpty(barCodeHVO.getBarcodeDList())) {
                        for (BarCodeDVO barCodeDVO : barCodeHVO.getBarcodeDList()) {
                            if (innerCheckTaskDVO.getPickTaskDId().equals(barCodeDVO.getPickTaskDId())) {
                                innerCheckTaskDVO.setLclNo(String.valueOf(barCodeHVO.getLclNo()));
                                innerCheckTaskDVO.setLclQty(barCodeDVO.getCheckQty());
                                innerCheckTaskDVO.setBarcodeDId(barCodeDVO.getBarcodeDId());
                            }
                        }
                    }
                }
            }
        }
        if (redisMap == null) {
            Map map = new HashMap();
            map.put("vo", JSON.toJSONString(innerCheckBillVO));
            map.put("items", JSON.toJSONString(itemList));
            redisTemplate.opsForValue().set(redisKey, map, 60, TimeUnit.MINUTES);
        }
        return innerCheckBillVO;
    }

    private void extractedItem(List<BaseMtlItemMst> itemList, InnerCheckTaskDV2VO innerCheckTaskDVO) {
        if (CollectionUtil.isEmpty(itemList)) {
            return;
        }
        BaseMtlItemMst baseMtlItemMst = itemList.stream().filter(t -> t.getItemId().equals(innerCheckTaskDVO.getItemId())).findFirst().orElse(null);
        if (null != baseMtlItemMst) {
            innerCheckTaskDVO.setItemCode(baseMtlItemMst.getItemCode());
            innerCheckTaskDVO.setItemName(baseMtlItemMst.getItemName());
            innerCheckTaskDVO.setItemAssistantCode(baseMtlItemMst.getItemAssistantCode());
            innerCheckTaskDVO.setSpecification(baseMtlItemMst.getSpecification());
            innerCheckTaskDVO.setCountryOriginId(baseMtlItemMst.getCountryOriginId());
            innerCheckTaskDVO.setManufacturerId(baseMtlItemMst.getManufacturerId());
            innerCheckTaskDVO.setCaseBarcode(baseMtlItemMst.getCaseBarcode());
            innerCheckTaskDVO.setPieceBarcode(baseMtlItemMst.getPieceBarcode());
            BaseMtlItemWms baseMtlItemWms = baseMtlItemMst.getBaseMtlItemWms();
            if (null != baseMtlItemWms) {
                innerCheckTaskDVO.setIsCaseOrPiece(baseMtlItemWms.getIsCaseOrPiece());
                innerCheckTaskDVO.setIsHosted(baseMtlItemWms.getIsHosted());
                innerCheckTaskDVO.setItemIsFragile(baseMtlItemWms.getItemIsFragile());
                innerCheckTaskDVO.setItemIsRecheck(baseMtlItemWms.getItemIsRecheck());
                innerCheckTaskDVO.setItemIsValuable(baseMtlItemWms.getItemIsValuable());
                // 是否序列号管理
                innerCheckTaskDVO.setItemIsSeque(baseMtlItemWms.getItemIsSeque());
            }
            BaseMtlItemHealth baseMtlItemHealth = baseMtlItemMst.getBaseMtlItemHealth();
            if (null != baseMtlItemHealth) {
                innerCheckTaskDVO.setIsCheck(baseMtlItemHealth.getIsCheck());
                innerCheckTaskDVO.setIsDelegate(baseMtlItemHealth.getIsDelegate());
                innerCheckTaskDVO.setItemHealthApprovalNbr(baseMtlItemHealth.getItemHealthApprovalNbr());
                innerCheckTaskDVO.setItemHealthListedLicensor(baseMtlItemHealth.getItemHealthListedLicensor());
                innerCheckTaskDVO.setItemHealthProduceLicense(baseMtlItemHealth.getItemHealthProduceLicense());
                innerCheckTaskDVO.setItemHealthIsDouble(baseMtlItemHealth.getItemHealthIsDouble() == null ? "0" : baseMtlItemHealth.getItemHealthIsDouble().toString());
                innerCheckTaskDVO.setItemHealthRecordNbr(baseMtlItemHealth.getItemHealthRecordNbr());
                innerCheckTaskDVO.setItemHealthRegisterNbr(baseMtlItemHealth.getItemHealthRegisterNbr());
                innerCheckTaskDVO.setItemHealthCategoryName(baseMtlItemHealth.getItemHealthCategoryName());
                innerCheckTaskDVO.setIsBacteria(baseMtlItemHealth.getIsBacteria());
            }
            //List<BaseMtlItemUomConversions> uomList = baseMtlItemMst.getBaseMtlItemUomConversionsList();
            RestMessage<Map<String, List<BaseMtlItemUomConversions>>> uomConversionsByItemIds = itemClient.getUomConversionsByItemIds(baseMtlItemMst.getItemId());
            List<BaseMtlItemUomConversions> uomList = uomConversionsByItemIds.getData().get(baseMtlItemMst.getItemId());
            if (uomList != null && uomList.size() > 0) {
                innerCheckTaskDVO.setBarCode(uomList.get(0).getBarcode());
                innerCheckTaskDVO.setBarcodeOld(uomList.get(0).getBarcodeOld());
                innerCheckTaskDVO.setMiddleSu("1");
                innerCheckTaskDVO.setMinSu("1");
                for (BaseMtlItemUomConversions uomConversions : uomList) {
                    if ("Y".equals(uomConversions.getMaxSu())) {
                        innerCheckTaskDVO.setMaxSu(uomConversions.getConversionRate().toString());
                    }
                    if (!"Y".equals(uomConversions.getMinSu()) && !"Y".equals(uomConversions.getMaxSu())) {
                        innerCheckTaskDVO.setMiddleSu(uomConversions.getConversionRate().toString());
                    }
                    if ("Y".equals(uomConversions.getMinSu()) && !"Y".equals(uomConversions.getMaxSu())) {
                        innerCheckTaskDVO.setMinSu(uomConversions.getConversionRate().toString());
                    }
                }
            }
            BaseMtlItemProfiles baseMtlItemProfiles = baseMtlItemMst.getBaseMtlItemProfiles();
            if (null != baseMtlItemProfiles) {
                innerCheckTaskDVO.setErpItemBarcode(baseMtlItemProfiles.getErpItemBarcode());
            }
        }
    }

    private boolean isChecked(InnerCheckTaskDV2VO pickTask) {
        BigDecimal redQty = Optional.ofNullable(pickTask.getRedQty()).orElse(BigDecimal.ZERO);
        BigDecimal pickQty = Optional.ofNullable(pickTask.getPickQty()).orElse(BigDecimal.ZERO);
        if ("1".equals(pickTask.getItemHealthIsDouble())) {
            BigDecimal checkQty2 = Optional.ofNullable(pickTask.getCheck2Qty()).orElse(BigDecimal.ZERO);
            boolean equals = checkQty2.add(redQty).equals(pickQty);
            if (!equals) {
                return false;
            }
        }
        BigDecimal checkQty = Optional.ofNullable(pickTask.getCheckQty()).orElse(BigDecimal.ZERO);
        return checkQty.add(redQty).equals(pickQty);
    }

    @Override
    @Transactional
    public LclVO queryLcl(
            String stationCode,
            String billGroupId,
            Integer lclNo,
            String containerCode,
            Integer isDoubleCheck,
            List<BaseMtlItemMst> itemList
    ) {
        UserDTO userDTO = userTemplate.getUserDTO();
        LclVO lclVO = new LclVO();
        String checkerNo = (isDoubleCheck != null && isDoubleCheck == 0 && !StringUtils.isBlank(containerCode)) ? userDTO.getUserBizModel().getEmployeeNo() : null;
        List<BarcodeHDO> lclNos = barcodeHMapper.getLclNoV2(stationCode, billGroupId, null, checkerNo);
        if (lclNos.isEmpty()) {
            log.info("当前订单暂无拼箱");
            //无拼箱则新增一个拼箱
            BarcodeHDO barcodeHDO = lclFunc(stationCode, billGroupId, containerCode, null, 1);
            BarCodeHVO barCodeHVO = new BarCodeHVO();
            barCodeHVO.setBarcode(barcodeHDO.getBarcode());
            barCodeHVO.setBarcodeDList(new ArrayList<>());
            barCodeHVO.setLclNo(barcodeHDO.getLclNo());
            barCodeHVO.setBillGroupId(billGroupId);
            barCodeHVO.setStationCode(stationCode);
            barCodeHVO.setStatus(barcodeHDO.getStatus());
            barCodeHVO.setType(barcodeHDO.getType());
            lclVO.setCurrLclNo(barcodeHDO.getLclNo());
            lclVO.setLcls(Collections.singletonList(barCodeHVO));
            return lclVO;
        }
        BarcodeHDO barcodeHDO = lclNos.stream().filter(var -> var.getStatus() == 0).findFirst().orElse(null);
        Integer currLclNo;
        if (null != barcodeHDO) {
            currLclNo = barcodeHDO.getLclNo();
        } else {
            //排序，获取最大拼箱号
            Optional<BarcodeHDO> maxLclNo = lclNos.stream().max(Comparator.comparing(BarcodeHDO::getLclNo));
            currLclNo = maxLclNo.get().getLclNo();
        }
        List<BarCodeHVO> collect = lclNos.stream().map(this::getLclVo).collect(Collectors.toList());
        List<PickTaskD> list = new ArrayList<>();
        Set<String> itemIdSet = new HashSet<>();
        for (BarCodeHVO barCodeHVO : collect) {
            List<BarCodeDVO> barCodeDVOList = new ArrayList<>();
            List<BarCodeDDO> barCodeDDOList = barcodeDMapper.findByMap(MapUtils.put("barcodeId", barCodeHVO.getId()).getMap(), BarCodeDDO.class);
            if (CollUtil.isEmpty(barCodeDDOList)) {
                continue;
            }
            for (BarCodeDDO barCodeDDO : barCodeDDOList) {
                //根据barcodedid查询picktask
                List<PickTaskD> pickTaskDList = pickTaskDMapper.findByMap(MapUtils.put("barcodeDId", barCodeDDO.getId()).getMap(), PickTaskD.class);
                if (CollUtil.isEmpty(pickTaskDList)) {
                    //当前拼箱无数据
                    continue;
                }
                if (barCodeHVO.getLclNo() == 0) {
                    list.addAll(pickTaskDList);
                }
                //根据pickTaskD生成barcoded返回
                List<WaveStorageDO> waveStorageDOList = waveStorageMapper.findByIds(WaveStorageDO.class, pickTaskDList.stream().map(PickTaskD::getWaveStorageId).distinct().toArray(), null);
                for (PickTaskD pickTaskD : pickTaskDList) {
                    BarCodeDVO barCodeDVO = new BarCodeDVO();
                    BeanUtil.copyProperties(barCodeDDO, barCodeDVO);
                    WaveStorageDO waveStorageDO = waveStorageDOList.stream().filter(var -> var.getId().equals(pickTaskD.getWaveStorageId())).findFirst().orElse(null);
                    if (null == waveStorageDO) {
                        throw new BizException("获取预占信息失败");
                    }
                    barCodeDVO.setItemId(waveStorageDO.getItemId());
                    barCodeDVO.setQty(pickTaskD.getPickQty());
                    barCodeDVO.setContainerCode(containerCode);
                    barCodeDVO.setBatchNum(waveStorageDO.getBatchNum());
                    barCodeDVO.setBarcodeDId(barCodeDDO.getId());
                    barCodeDVO.setCheckQty((pickTaskD.getChecker2Qty() == null || pickTaskD.getChecker2Qty().doubleValue() == 0) ? pickTaskD.getCheckQty() : pickTaskD.getChecker2Qty());
                    itemIdSet.add(waveStorageDO.getItemId());
                    barCodeDVO.setItemId(waveStorageDO.getItemId());
                    barCodeDVO.setPickTaskDId(pickTaskD.getPickTaskDId());
                    barCodeDVO.setPrintCount(barCodeDDO.getPrintCnt());
                    barCodeDVOList.add(barCodeDVO);
                }
            }
            barCodeHVO.setBarcodeDList(barCodeDVOList);
        }
        List<BaseMtlItemMst> items = new ArrayList<>();
        if (CollUtil.isNotEmpty(itemIdSet)) {
            RestMessage<List<BaseMtlItemMst>> listByIds = itemClient.getListByIds(itemIdSet);
            items = listByIds.getData();
        }

        for (BarCodeHVO barCodeHVO : collect) {
            for (BarCodeDVO barCodeDVO : barCodeHVO.getBarcodeDList()) {
                if (items != null && items.size() > 0) {
                    for (BaseMtlItemMst waveItemInfoVO : items) {
                        if (barCodeDVO.getItemId().equals(waveItemInfoVO.getItemId())) {
                            barCodeDVO.setItemCode(waveItemInfoVO.getItemCode());
                            barCodeDVO.setItemName(waveItemInfoVO.getItemName());
                            List<BaseMtlItemUomConversions> uomList = waveItemInfoVO.getBaseMtlItemUomConversionsList();
                            if (uomList != null && uomList.size() > 0) {
                                barCodeDVO.setCaseConversionRate(uomList.get(0).getConversionRate());
                            }
                        }
                    }
                }
            }
        }

        if (lclNos.get(lclNos.size() - 1).getLclNo() != 0) {
            lclVO.setLcls(collect);
        } else {
            List<BarCodeDVO> caseBarcodeDList = new ArrayList<>();
            List<BarCodeHVO> pieceBarCodeHVOList = new ArrayList<>();
            for (BarCodeHVO barCodeHVO : collect) {
                if (barCodeHVO.getLclNo() != 0) {
                    pieceBarCodeHVOList.add(barCodeHVO);
                } else {
                    List<BarCodeDVO> barcodeDList = barCodeHVO.getBarcodeDList();
                    for (BarCodeDVO barCodeDVO : barcodeDList) {
                        PickTaskD d = list.stream().filter(t -> t.getPickTaskDId().equals(barCodeDVO.getPickTaskDId())).
                                findFirst().orElse(null);
                        if (d != null) {
                            barCodeDVO.setPrintCount(d.getPrintCount());
                            barCodeDVO.setUomQty(barCodeDVO.getQty().divide(barCodeDVO.getCaseConversionRate(), RoundingMode.DOWN));
                        }
                    }
                    caseBarcodeDList.addAll(barcodeDList);
                }
            }
            BarCodeHVO barCodeHVO = new BarCodeHVO();
            BeanUtils.copyProperties(collect.get(0), barCodeHVO);
            barCodeHVO.setLclNo(0);
            barCodeHVO.setBarcode(null);
            barCodeHVO.setBarcodeDList(caseBarcodeDList);
            List<BarCodeHVO> retCollect = new ArrayList<>();
            retCollect.add(barCodeHVO);
            retCollect.addAll(pieceBarCodeHVOList);
            lclVO.setLcls(retCollect);
        }
        lclVO.setCurrLclNo(currLclNo);
        return lclVO;
    }

    private BarCodeHVO getLclVo(BarcodeHDO lcl) {
        BarCodeHVO barCodeHVO = new BarCodeHVO();
        BeanUtils.copyProperties(lcl, barCodeHVO);
        String barcodeId = lcl.getId();
        List<BarCodeDDO> barCodeDDOS = barcodeDMapper.findByMap(MapUtils.put("barcodeId", barcodeId).getMap(),
                BarCodeDDO.class);
        List<BarCodeDVO> barCodeDVOs = barCodeDDOS.stream().filter(p -> p.getQty() != null && p.getQty().compareTo(BigDecimal.ZERO) > 0).
                map(barCodeDDO -> {
                    BarCodeDVO barCodeDVO = new BarCodeDVO();
                    barCodeDVO.setBarcodeDId(barCodeDDO.getId());
                    barCodeDVO.setPickTaskDId(barCodeDDO.getWmsPickTaskDId());
                    barCodeDVO.setContainerCode(barCodeDDO.getContainerCode());
                    BeanUtils.copyProperties(barCodeDDO, barCodeDVO);
                    barCodeDVO.setPrintCount(lcl.getPrintCount());
                    return barCodeDVO;
                }).collect(Collectors.toList());
        barCodeHVO.setBarcodeDList(barCodeDVOs);
        return barCodeHVO;
    }

    private void updateFlowStatus(String pickTaskId, int flowStatus) {
        Criteria crt = Criteria.forClass(PickTaskD.class);
        Restriction r1 = Restrictions.eq("pickTaskId", pickTaskId);
        Restriction r2 = Restrictions.eq("enterpriseId", userTemplate.getUserDTO().getEnterpriseId());
        crt.setRestriction(Restrictions.and(r1, r2));
        pickTaskDMapper.updateMapByCriteria(MapUtils.put("flowStatus", flowStatus).getMap(), crt);
    }


    @Override
    @Transactional
    @TxTransaction(isStart = true)
    @Flow(name = "红冲", category = "出库/复核管理/内复核/红冲", index = 3, description = "1、校验 2、生成WMS_SALE_RED_RECORD记录 " +
            "3、非整条明细红冲更新拼箱数量(WMS_BARCODE_D qty) 4、整条明细红冲删除WMS_BARCODE_D记录 5、整箱红冲删除WMS_BARCODE_H")
    public RestMessage<InnerCheckBillV2VO> makeCheckRed(CheckMakeRedDTO redDTO) {
        PickTaskD pickTaskD = new PickTaskD();
        PickTaskH pickTaskH = new PickTaskH();
        validRedFunc(redDTO, pickTaskD, pickTaskH);
        return redFunc(redDTO, pickTaskD, pickTaskH);
    }

    @FuncImpl(name = "红冲校验", func = ValidRedFunc.class, description = "红冲校验")
    public void validRedFunc(CheckMakeRedDTO redDTO, PickTaskD pickTaskD, PickTaskH pickTaskH) {
        String billId = redDTO.getBillId();
        String srcId = redDTO.getSrcId();
        String billDId = redDTO.getBillDId();
        String srcLineId = redDTO.getSrcLineId();
        String reason = redDTO.getRedReason();
        BigDecimal redQty = redDTO.getRedQty();
        String pickTaskDId = redDTO.getPickTaskDId();
        String stationCode = redDTO.getStationCode();
        String containerCode = redDTO.getContainerCode();
        // 设置srcId;
        PickTaskD pickTaskDInfo = pickTaskDMapper.findById(pickTaskDId, PickTaskD.class);
        PickTaskH pickTaskHInfo = pickTaskHMapper.findById(pickTaskDInfo.getPickTaskId(), PickTaskH.class);
        List<WaveBillDO> billDOs = billMapper.findByMap(MapUtils.put("billGroupId", pickTaskHInfo.getBillGroupId()).getMap(), WaveBillDO.class);
        WaveBillDDO billDDO = billDMapper.findById(billDId, WaveBillDDO.class);
        if (billDDO != null) {
            srcLineId = billDDO.getSrcLineId();
            redDTO.setSrcLineId(srcLineId);
        }

        if (CollectionUtil.isNotEmpty(billDOs)) {
            Map<String, Object> map = MapUtils.put("lineid", srcLineId).getMap();
            List<OmsDoBillDDO> omsDoBillDDOS = omsDoBillDMapper.findByMap(map, OmsDoBillDDO.class);
            if (CollectionUtil.isNotEmpty(omsDoBillDDOS)) {
                srcId = omsDoBillDDOS.get(0).getBillid();
            }
            redDTO.setSrcId(srcId);
        }

        AssertUtils.isNotNull(billId, billDId, srcId, srcLineId, reason, containerCode, stationCode, pickTaskDId);
        log.debug("冲红操作，原订单id 时 {}  ,订单行id={}", srcId, srcLineId);
        BeanUtils.copyProperties(pickTaskDInfo, pickTaskD);
        BeanUtils.copyProperties(pickTaskHInfo, pickTaskH);
        if (pickTaskH.getPickTaskType() == 3 && pickTaskD.getPickTaskType() == 0) {
            // 整单拣选
            // WaveBillDDO billDDO = billDMapper.findById(billDId, WaveBillDDO.class);
            BigDecimal[] results = redQty.divideAndRemainder(billDDO.getCaseConversionRate());
            if (results[1].compareTo(BigDecimal.ZERO) != 0) {
                throw new BizException("整件不能红冲零散件！");
            }
        }
    }

    @FuncImpl(name = "红冲", func = RedFunc.class, description = "红冲")
    public RestMessage<InnerCheckBillV2VO> redFunc(CheckMakeRedDTO redDTO, PickTaskD pickTaskD, PickTaskH pickTaskH) {
        String billId = redDTO.getBillId();
        String srcId = redDTO.getSrcId();
        String billDId = redDTO.getBillDId();
        String srcLineId = redDTO.getSrcLineId();
        String reason = redDTO.getRedReason();
        BigDecimal redQty = redDTO.getRedQty();
        if (null == redQty || redQty.compareTo(BigDecimal.ZERO) < 0) {
            throw new BizException("红冲数量不正确！红冲数量 = " + redQty);
        }
        String pickTaskDId = redDTO.getPickTaskDId();
        String stationCode = redDTO.getStationCode();
        String containerCode = redDTO.getContainerCode();
        UserDTO userDTO = userTemplate.getUserDTO();
        // 内复核冲红
        SaleRedRecord saleRedRecord = new SaleRedRecord();
        saleRedRecord.setSrcLineId(srcLineId);
        saleRedRecord.setBillDId(billDId);
        saleRedRecord.setBillId(billId);
        saleRedRecord.setCheckerName(userDTO.getNickname());
        saleRedRecord.setCheckerId(userDTO.getId());
        saleRedRecord.setCheckerNo(userDTO.getUserBizModel().getEmployeeNo());
        saleRedRecord.setSrcId(srcId);
        saleRedRecord.setQty(redQty);
        String warehouseId = pickTaskH.getWarehouseId();
        saleRedRecord.setWarehouseId(warehouseId);
        saleRedRecord.setType(4);
        saleRedRecord.setReason(redDTO.getRedReason());
        saleRedRecord.setPickTaskDId(pickTaskDId);
        ModelUtils.setBaseInfo(saleRedRecord, ModelUtils.OpType.INSERT, userDTO);
        saleRedRecordMapper.save(saleRedRecord);

        List<SaleRedRecord> redRecords = saleRedRecordMapper.findByMap(MapUtils.put("pickTaskDId", pickTaskDId).put("type", 4).getMap(), SaleRedRecord.class);
        if (redRecords != null && redRecords.size() > 0) {
            BigDecimal lineRedQty = redRecords.stream().map(SaleRedRecord::getQty).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal subtract = pickTaskD.getPickQty().subtract(lineRedQty);
            if (subtract.compareTo(BigDecimal.ZERO) < 0) {
                throw new BizException("复核数量不能为负数！");
            }
            //无论什么状态都能红冲，仅仅记录红冲数量
            Map<String, Object> map = new HashMap<>();
            map.put("saleRedQty", lineRedQty);
            if (lineRedQty.compareTo(pickTaskD.getQty()) >= 0) {
                map.put("flowStatus", "5");
                map.put("checkQty", 0);
                map.put("checkerName", userDTO.getNickname());
                map.put("checkerId", userDTO.getId());
                map.put("checkerNo", userDTO.getUserBizModel().getEmployeeNo());
                map.put("checkTime", DateUtil.date().toString());
            }
            pickTaskDMapper.updateMapById(pickTaskDId, map, PickTaskD.class);
            /*
            河南省医药由于一个barcoded打印一张标签，因此逻辑改动较大。
            整件拣货：一个pick_task_d会对应多个barcoded。(根据标签拆分配置，当一个pick_task_d有多件且小于配置值时，会生成多个barcoded)
            零货拣货：一个barcoded会对应多个pick_task_d，但是一个barcodeh只会对应一个barcoded。(一个barcoded相当于一个拼箱，一个拼箱可以装多个pick_task_d)
            整散合一：等同于零货处理。
            所以此处需要判断拣货类型，如果是整件，则可用执行以下逻辑，非整件，则修改pick_task_d的红冲数量
             */
            if (pickTaskD.getPickTaskType() == 0) {
                Criteria crt = Criteria.forClass(BarCodeDDO.class);
                Restriction r1 = Restrictions.eq("wmsPickTaskDId", pickTaskDId);
                crt.setRestriction(Restrictions.and(r1));
                if (subtract.compareTo(BigDecimal.ZERO) != 0) {
                    barcodeDMapper.updateMapByCriteria(MapUtils.put("qty", subtract).getMap(), crt);
                } else {
                    List<BarCodeDDO> list = barcodeDMapper.findByCriteria(crt);
                    if (list != null && list.size() == 1) {
                        String barcodeId = list.get(0).getBarcodeId();
                        long count = barcodeDMapper.findCountByMap(MapUtils.put("barcodeId", barcodeId).getMap(), BarCodeDDO.class);
                        if (count == 1) {
                            barcodeHMapper.deleteById(barcodeId, BarcodeHDO.class);
                        }
                    }
                    barcodeDMapper.deleteByCriteria(crt);
                }
            }
        }

        // 盘点库货位id
        String locatorId = null;
        Integer redDoMethod = redDTO.getRedDoMethod();
        // 转移到盘点库
        if (redDoMethod != null && redDoMethod == 1) {
            RestMessage<String> rest = warehouseResource.getLocatorIdByCategory("PDK", warehouseId);
            if (!rest.isSuccess() || rest.getData() == null) {
                throw new PrologException("查询盘点库货位出错,或货位为空:" + rest.getMessage());
            }
            locatorId = rest.getData();
        }
        // 冲红调整库存
        this.updateStock(billId, billDId, redQty, pickTaskD, locatorId);
        // 该sql等于0标识整单红冲
        long wholeBillMakeRed = saleRedRecordMapper.isWholeBillMakeRed(srcId);

        // 修复有整散，零货任务都红冲了外复核索取不到问题
        // 1表示该TaskH下的明细都红冲了
        long taskHRed = saleRedRecordMapper.isWholeTaskHRed(pickTaskH.getPickTaskId());

        if (wholeBillMakeRed == 0) {
            log.info("整单已经被红冲" + srcId);
            Map<String, Object> isCancelled = MapUtils.put("isCancelled", 1).put("partlyRed", null).getMap();
            omsDoBillHMapper.updateMapById(srcId, isCancelled, OmsDoBillHDO.class);
            Criteria cr = Criteria.forClass(WaveBillDO.class);
            cr.restrictionBuilder().eq("srcId", srcId).eq("isMerged", 1);
            cr.buildRestriction();
            if (billMapper.findCountByCriteria(cr) == 0L) {
                // 不是合单的
                Criteria crt1 = Criteria.forClass(PickTaskH.class);
                Restriction r1 = Restrictions.eq("billId", billId);
                Restriction r2 = Restrictions.eq("warehouseId", warehouseId);
                Restriction r3 = Restrictions.ne("status", "10");
                crt1.setRestriction(Restrictions.and(r1, r2, r3));
                pickTaskHMapper.updateMapByCriteria(MapUtils.put("status", "10").getMap(), crt1);
            }

            // 释放容器
            ContainerQueryVO containerQueryVO = new ContainerQueryVO();
            containerQueryVO.setContainerCode(containerCode);
            containerQueryVO.setWarehouseId(warehouseId);
            warehouseResource.release(containerQueryVO);
            // ERP回传方式写中间表，通过mq立刻通知oms处理，使用 PostBackErpHandle by Huanglh 20220112
            /*EtlMessage etlMessage = new EtlMessage();
            etlMessage.setId(srcId);
            etlMessage.setEtlType(EtlTypeConstants.out_do_w2o);
            try {
                exchangeSender.send(etlMessage);
            } catch (Exception e) {
                throw new BizException("内复核红冲变为整单红冲 回传ERP，rabbitmq发送消息失败！");
            }*/
            // 红冲只需要需要判断一个业务配置参数
            IStrategyHandle strategyHandle = strategySelector.selectHandle(Collections.singletonList(STRATEGY_CODE.POST_BACK_ERP__MQ__ON), userDTO.getEnterpriseId(), warehouseId, "do");
            strategyHandle.execute(Collections.singletonList(srcId), "内复核红冲变为整单红冲 回传ERP，rabbitmq发送消息失败！");
            return RestMessage.doSuccess(new InnerCheckBillV2VO());
        }
        InnerCheckBillV2VO containerInfo = this.findPickupByContainerFuncV2(
                containerCode,
                stationCode,
                null,
                null,
                redDTO.getLclNo(),
                redDTO.getIsDoubleCheck(),
                warehouseId
        );
        if (taskHRed == 1) {
            // 更新taskH为10
            pickTaskHMapper.updateMapById(pickTaskH.getPickTaskId(), MapUtils.put("status", "10").getMap(), PickTaskH.class);
        }
        return RestMessage.doSuccess(containerInfo);
    }

    // 冲红调整库存
    private void updateStock(String billId, String billDId, BigDecimal redQty, PickTaskD innerCheckDDO, String locatorId) {
        ValidList<InvStockOut> validList = new ValidList<>();
        InvStockOut invStockOut = new InvStockOut();

        validList.add(invStockOut);
        List<InvStockOutDetail> details = new ArrayList<>();
        InvStockOutDetail detail = new InvStockOutDetail();
        details.add(detail);
        invStockOut.setDetails(details);

        invStockOut.setBillId(billId);
        invStockOut.setLineBillId(billDId);
        invStockOut.setWarehouseId(innerCheckDDO.getWarehouseId());

        List<WaveStorageDO> storageDOS = storageMapper.getStorageByPickTaskDId(innerCheckDDO.getPickTaskDId());
        if (storageDOS.size() != 1) {
            throw new PrologException("找不到对应的库存预占信息");
        }
        WaveStorageDO waveStorageDO = storageDOS.get(0);
        detail.setBatchId(waveStorageDO.getBatchId());
        detail.setQuantity(redQty);
        detail.setLocatorId(waveStorageDO.getLocatorId());
        if (locatorId != null) {
            detail.setExtParams(MapUtils.put("adjustType", "2").put("takeLocatorId", locatorId).getMap());
        }
        // 转移盘点库时状态改为6，盘点
        // 无实物移至盘点库
        detail.setSubStatus("1");
        // detail.setStatus("6");
        RestMessage restMessage = inventoryResource.redAdjustStock(validList);
        if (!restMessage.isSuccess()) {
            log.error("红冲失败： {},请求如下：{} ", restMessage.getMessage(), invStockOut);
            throw new BizException("红冲失败: " + restMessage.getMessage());
        }
        // 通过billId查询得到订单数据
        List<WmsAdjustAddVO> adjustAddVOList = new ArrayList<>();
        WaveBillDO bill = billMapper.findById(billId, WaveBillDO.class);
        WaveBillDDO billD = billDMapper.findById(billDId, WaveBillDDO.class);
        if (null != locatorId) {
            // 无计划损溢VO
            WmsAdjustAddVO wmsAdjustAddVO = new WmsAdjustAddVO();
            wmsAdjustAddVO.setDcId(waveStorageDO.getBldgId());
            wmsAdjustAddVO.setWarehouseId(waveStorageDO.getWarehouseId());
            wmsAdjustAddVO.setCargoOwnerId(bill.getConsignorId());
            wmsAdjustAddVO.setWhLocatorId(locatorId);
            wmsAdjustAddVO.setItemId(billD.getItemId());
            wmsAdjustAddVO.setBatchId(waveStorageDO.getBatchId());
            wmsAdjustAddVO.setProductionDate(waveStorageDO.getProductionDate());
            wmsAdjustAddVO.setBatchExpiredDate(waveStorageDO.getExpiredDate());
            wmsAdjustAddVO.setQuantity(redQty);
            wmsAdjustAddVO.setConversionRate(billD.getConversionRate());
            wmsAdjustAddVO.setBatchNum(waveStorageDO.getBatchNum());
            wmsAdjustAddVO.setUom(billD.getCaseUom());
            wmsAdjustAddVO.setStockStatus(billD.getStockStatus());
            wmsAdjustAddVO.setAdjustType("1"); // 无计划盘点
            wmsAdjustAddVO.setManufacturerLotNum(billD.getManufacturerLotNum());
            wmsAdjustAddVO.setPieceQuantity(redQty); // 损溢数量
            wmsAdjustAddVO.setPieceAccount(redQty);
            wmsAdjustAddVO.setPieceAccountFact(BigDecimal.ZERO);
            wmsAdjustAddVO.setType(1); // 数量调整
            wmsAdjustAddVO.setIsAdd(0); // 是否是是新增账存(0否1是)
            wmsAdjustAddVO.setAdjustHeaderId(billId);
            wmsAdjustAddVO.setAutoBillNum(bill.getSrcCode());
            adjustAddVOList.add(wmsAdjustAddVO);
        }
        if (CollectionUtil.isNotEmpty(adjustAddVOList)) {
            for (WmsAdjustAddVO wmsAdjustAddVO : adjustAddVOList) {
                RestMessage<?> rest = operateResource.saveAdjust(wmsAdjustAddVO);
                if (null == rest || !rest.isSuccess()) {
                    throw new BizException("生成无计划损溢失败！" + JSONObject.toJSONString(rest) + "参数 = " + JSONObject.toJSONString(wmsAdjustAddVO));
                }
            }
        }
    }

    @Override
    @Transactional
    @TxTransaction
    public RestMessage<InnerCheckBillV2VO> checkItemByNum(CheckInfoDTO checkInfoDTO) {
        List<PickTaskD> pickTaskDList = Lists.newArrayList();
        ItemBarCodeVO itemBarCodeVO = new ItemBarCodeVO();
        validInnerCheckFunc(checkInfoDTO, false, true, false, pickTaskDList, itemBarCodeVO);
        updatePickupDInnerCheckInfoFunc(checkInfoDTO, false, true, false, pickTaskDList, itemBarCodeVO);
        return createBarcodeFunc(checkInfoDTO, false, true, false, pickTaskDList, itemBarCodeVO);
    }

    // 扫码复核
    @Override
    @Transactional
    @TxTransaction
    @Flow(name = "商品内复核", category = "出库/复核管理/内复核/商品内复核", index = 2, description = "1、数据校验2、更新拼箱数量WMS_PICK_TASK_D" +
            "3、判断是否为2次复核，二次复核不生成拼箱数据，一次复核生成拼箱数据")
    public RestMessage<InnerCheckBillV2VO> checkItem(CheckInfoDTO checkInfoDTO) {
        List<PickTaskD> pickTaskDList = Lists.newArrayList();
        ItemBarCodeVO itemBarCodeVO = new ItemBarCodeVO();
        validInnerCheckFunc(checkInfoDTO, true, false, false, pickTaskDList, itemBarCodeVO);
        updatePickupDInnerCheckInfoFunc(checkInfoDTO, false, true, false, pickTaskDList, itemBarCodeVO);
        return createBarcodeFunc(checkInfoDTO, true, false, false, pickTaskDList, itemBarCodeVO);
    }

    public void validInnerCheckFunc(
            CheckInfoDTO checkInfoDTO,
            boolean byScan,
            boolean isByNum,
            boolean redCheck,
            List<PickTaskD> pickTaskDList,
            ItemBarCodeVO itemBarCodeVO
    ) {
        String itemBarcode = checkInfoDTO.getItemBarcode();
        String containerCode = checkInfoDTO.getContainerCode();
        String stationCode = checkInfoDTO.getStationCode();
        String billGroupId = checkInfoDTO.getBillGroupId();
        String warehouseId = checkInfoDTO.getWarehouseId();
        List<CheckPickTaskDDTO> checkPickTaskDDTOList = checkInfoDTO.getCheckPickTaskDDTOList();
        AssertUtils.isNotNull(warehouseId, containerCode, stationCode, billGroupId, checkPickTaskDDTOList);
        List<String> pickTaskDIds = checkPickTaskDDTOList.stream().map(CheckPickTaskDDTO::getPickTaskDId).collect(Collectors.toList());

        List<WaveStorageDO> storageDOS = storageMapper.getStorageByPickTaskDIds(pickTaskDIds);
        if (storageDOS.size() != pickTaskDIds.size()) {
            throw new PrologException("对应的库存预占信息不匹配");
        }
        WaveStorageDO waveStorageDO = storageDOS.get(0);
        ItemBarCodeVO data = itemClient.getItemByBarCode(checkInfoDTO.getItemBarcode(), waveStorageDO.getItemId()).getData();
        if (data == null) {
            throw new ParameterException("该商品信息不存在");
        }
        BeanUtils.copyProperties(data, itemBarCodeVO);
        // PickTaskD byId = pickTaskDMapper.findById(pickTaskDId, PickTaskD.class);
        List<PickTaskD> pickTaskDListByIds = pickTaskDMapper.findByIds(PickTaskD.class, pickTaskDIds.toArray(new String[pickTaskDIds.size()]), null);
        if (pickTaskDIds.size() != pickTaskDListByIds.size()) {
            throw new BizException("找不到对应的复核信息,明细数量不匹配");
        }
        pickTaskDList.addAll(pickTaskDListByIds);

        Map<String, PickTaskD> pickTaskDMap = pickTaskDListByIds.stream().collect(Collectors.toMap(PickTaskD::getPickTaskDId, Function.identity()));
        for (CheckPickTaskDDTO checkPickTaskDDTO : checkPickTaskDDTOList) {
            PickTaskD pickTaskD = pickTaskDMap.get(checkPickTaskDDTO.getPickTaskDId());

            BigDecimal inputQty = checkPickTaskDDTO.getQty();
            Criteria criteria = Criteria.forClass(SaleRedRecord.class);
            criteria.restrictionBuilder().eq("pickTaskDId", checkPickTaskDDTO.getPickTaskDId()).eq("type", 4);
            criteria.buildRestriction();
            List<SaleRedRecord> redRecords = saleRedRecordMapper.findByCriteria(criteria);
            if (redRecords != null && redRecords.size() > 0) {
                BigDecimal lineRedQty = redRecords.stream().map(SaleRedRecord::getQty).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal allPickQty = pickTaskD.getPickQty();
                BigDecimal subtract = allPickQty.subtract(lineRedQty);
                if (subtract.subtract(inputQty).compareTo(BigDecimal.ZERO) != 0) {
                    throw new BizException("数据异常：复核数量 != 拣货数量-红冲数量, " + checkPickTaskDDTO.getPickTaskDId());
                }
            }
        }

    }

    public void updatePickupDInnerCheckInfoFunc(
            CheckInfoDTO checkInfoDTO,
            boolean byScan,
            boolean isByNum,
            boolean redCheck,
            List<PickTaskD> pickTaskDList,
            ItemBarCodeVO itemBarCodeVO
    ) {
        String redisKey = "wms:pick:serial:" + DateUtil.format(DateUtil.date(), DatePattern.NORM_DATE_FORMAT);
        RedisAtomicInteger counter = new RedisAtomicInteger(redisKey, redisTemplate.getConnectionFactory());
        counter.expireAt(DateUtil.endOfDay(DateUtil.date()));
        String itemBarcode = checkInfoDTO.getItemBarcode();
        String containerCode = checkInfoDTO.getContainerCode();
        String stationCode = checkInfoDTO.getStationCode();
        String billGroupId = checkInfoDTO.getBillGroupId();
        AssertUtils.isNotNull(containerCode, stationCode, billGroupId);
        UserDTO userDTO = userTemplate.getUserDTO();
        String nickName = userDTO.getNickname();
        //K=pick_task_d_id,V=qty
        Map<String, BigDecimal> checkPickTaskDMap = checkInfoDTO.getCheckPickTaskDDTOList().stream().collect(Collectors.toMap(CheckPickTaskDDTO::getPickTaskDId, CheckPickTaskDDTO::getQty));

        Set<String> pickTaskIdSet = pickTaskDList.stream().map(PickTaskD::getPickTaskDId).collect(Collectors.toSet());
        if (CollUtil.isEmpty(pickTaskIdSet)) {
            throw new BizException("请确认需要复核的商品！");
        }
        List<PickTaskD> pickTaskDListDB = pickTaskDMapper.findByIds(PickTaskD.class, pickTaskIdSet.toArray(new String[]{}), null);
        if (CollUtil.isEmpty(pickTaskDListDB)) {
            throw new BizException("请确认需要复核的商品！");
        }

        for (PickTaskD taskD : pickTaskDList) {
            PickTaskD pickTaskD = pickTaskDListDB.stream().filter(var -> var.getPickTaskDId().equals(taskD.getPickTaskDId())).findFirst().orElse(null);
            if (null == pickTaskD) {
                continue;
            }
            pickTaskD.setCheckTime(new Date());
            if (pickTaskD.getFlowStatus() == null) {
                throw new BizException("当前拣货任务状态错误，请反馈！");
            }
            if (checkInfoDTO.getIsDoubleCheck() == 1 && (pickTaskD.getFlowStatus() != null && !pickTaskD.getFlowStatus().equals("5"))) {
                throw new BizException("当前商品状态不能进行二次复核！");
            }
            if (checkInfoDTO.getIsDoubleCheck() != 1 && (pickTaskD.getFlowStatus() != null && pickTaskD.getFlowStatus().equals("5"))) {
                //当前商品已复核，跳过
                continue;
            }
            if (checkInfoDTO.getIsDoubleCheck() == 1) {
                pickTaskD.setCheckerName2(userDTO.getNickname());
                pickTaskD.setCheckerId2(userDTO.getId());
                pickTaskD.setCheckerNo2(userDTO.getUserBizModel().getEmployeeNo());
            } else {
                pickTaskD.setCheckerName(userDTO.getNickname());
                pickTaskD.setCheckerId(userDTO.getId());
                pickTaskD.setCheckerNo(userDTO.getUserBizModel().getEmployeeNo());
            }
            BigDecimal inputQty = checkPickTaskDMap.get(pickTaskD.getPickTaskDId());
            if (isByNum) {
                //校验人员和数量
                spellQty(nickName, pickTaskD, inputQty, checkInfoDTO, itemBarCodeVO);
            }

            BigDecimal newQty;
            if (byScan) {
                Integer isDoubleCheck = checkInfoDTO.getIsDoubleCheck();
                newQty = itemBarCodeVO.getConversionRate();
                if (isDoubleCheck == 1) {
                    newQty = newQty.add(pickTaskD.getCheckQty() == null ? BigDecimal.ZERO : pickTaskD.getCheckQty());
                } else {
                    newQty = newQty.add(pickTaskD.getChecker2Qty() == null ? BigDecimal.ZERO : pickTaskD.getChecker2Qty());
                }
                spellQty(nickName, pickTaskD, newQty, checkInfoDTO, itemBarCodeVO);
            }
            if (pickTaskD.getChecker2Qty() != null) {
                if (pickTaskD.getCheckQty() == null || pickTaskD.getCheckQty().compareTo(pickTaskD.getChecker2Qty()) < 0) {
                    throw new BizException("复核数量异常");
                }
            }
            pickTaskD.setFlowStatus("5");
            if ((isByNum && checkInfoDTO.getIsDoubleCheck() != 1) || (byScan && checkInfoDTO.getIsDoubleCheck() != 1)) {
                String itemId = itemBarCodeVO.getItemId();
                String itemCode = checkInfoDTO.getItemCode();
                AssertUtils.isNotBlank(itemCode);
                BarcodeHDO lclNo = getDefaultLclNo(stationCode, billGroupId, containerCode, pickTaskD);
                String lclHId = lclNo.getId();
                // 零货拼箱可以一个拼箱对应多个pick_task_d,因此barcodeh与barcoded在拼箱的时候是一一对应的，直接查询拼箱即可
                List<BarCodeDDO> dList = barcodeDMapper.findByMap(MapUtils.put("barcodeId", lclHId).getMap(), BarCodeDDO.class);
                if (dList.size() > 0) {
                    pickTaskD.setBarcodeDId(dList.get(0).getId());
                } else {
                    //如果没有明细则新增
                    BarCodeDDO barCodeDDO = new BarCodeDDO();
                    barCodeDDO.setBarcodeId(lclNo.getId());
                    PickTaskH h = pickTaskHMapper.findById(pickTaskD.getPickTaskId(), PickTaskH.class);
                    String slidwayNo;
                    String firstPreFix;
                    if (h.getType() == 1) {//补货
                        firstPreFix = "2";
                        // 鹰江 61 AGV零货区（AGVL），62零货货架区(LA)，63AGV托盘区（AGVZ）
                        RestMessage<List<BaseCommonDict>> replenishSlidMsg = commonClient.getDictBasicListByCodes("replenish_qy", null);
                        //查询目标货位所属库区
                        MoveTaskDO moveTaskDO = moveTaskMapper.getMoveTask(pickTaskD.getPickTaskDId(), pickTaskD.getWarehouseId());
                        RestMessage<List<BaseWmsWarehouseArea>> all = warehouseResource.getWarehouseAreaBasicInfos(new String[]{moveTaskDO.getDestWarehouseAreaId()});
                        if (!all.isSuccess() || CollUtil.isEmpty(all.getData())) {
                            throw new BizException("补货上架库存查询失败！");
                        }
                        BaseWmsWarehouseArea area = all.getData().get(0);
                        BaseCommonDict dict = replenishSlidMsg.getData().stream().filter(var -> var.getDictCode().equals(area.getWarehouseAreaCode())).findFirst().orElse(null);
                        if (null == dict) {
                            throw new BizException("当前库区未配置与滑道对应关系！");
                        }
                        slidwayNo = dict.getAttr1();
                    } else {//拣货
                        RestMessage<List<BaseCommonDict>> outSlidMsg = commonClient.getDictBasicListByCodes("OUT_LINE_QY", null);
                        //查询运输路线
                        String shipLineId = pickTaskHMapper.findShipLineId(h.getPickTaskId());
                        if (null == shipLineId) {
                            throw new BizException("当前订单没有运输路线！");
                        }
                        BaseCommonDict dict = outSlidMsg.getData().stream().filter(var -> var.getDictCode().equals(shipLineId)).findFirst().orElse(null);
                        if (null == dict) {
                            throw new BizException("当前运输路线未配置与滑道对应关系！");
                        }
                        slidwayNo = dict.getAttr1();
                        firstPreFix = "1";
                    }
                    Integer serialNo = counter.incrementAndGet();
                    barCodeDDO.setId(BillUtils.getBarcode(firstPreFix, "1", serialNo, slidwayNo, "PX"));
                    barCodeDDO.setEnterpriseId(userTemplate.getUserDTO().getEnterpriseId());
                    barCodeDDO.setWarehouseId(pickTaskD.getWarehouseId());
                    barCodeDDO.setIsPiece(1);
                    barCodeDDO.setCheckStatus("0");
                    barcodeDMapper.save(barCodeDDO);
                    dList.add(barCodeDDO);
                    pickTaskD.setBarcodeDId(dList.get(0).getId());
                }
//                updateLclD(stationCode, billGroupId, itemCode, itemId, inputQty, containerCode, pickTaskD);
            }
            pickTaskDMapper.update(pickTaskD);
        }
    }

    //内复核索取
    //当前功能会查询未复核完成的数据(取缓存)，当前容器号已被索取才能进行复核操作
    public RestMessage<InnerCheckBillV2VO> createBarcodeFunc(
            CheckInfoDTO checkInfoDTO,
            boolean byScan,
            boolean isByNum,
            boolean redCheck,
            List<PickTaskD> pickTaskDList,
            ItemBarCodeVO itemBarCodeVO
    ) {
        String itemBarcode = checkInfoDTO.getItemBarcode();
        String containerCode = checkInfoDTO.getContainerCode();
        String stationCode = checkInfoDTO.getStationCode();
        String billGroupId = checkInfoDTO.getBillGroupId();
        String warehouseId = checkInfoDTO.getWarehouseId();

        InnerCheckBillV2VO containerInfo = findPickupByContainerFuncV2(
                containerCode,
                stationCode,
                checkInfoDTO.getCheckPickTaskDDTOList(),
                billGroupId,
                checkInfoDTO.getLclNo(),
                checkInfoDTO.getIsDoubleCheck(),
                warehouseId
        );
        return RestMessage.doSuccess(containerInfo);
    }


    private void spellQty(
            String nickName,
            PickTaskD pickTaskD,
            BigDecimal qty,
            CheckInfoDTO checkInfoDTO,
            ItemBarCodeVO itemByBarCode
    ) {
        BigDecimal pickQty = pickTaskD.getPickQty();
        if (pickQty.compareTo(qty) < 0) {
            throw new BizException("复核数量已经超过拣货数量，请核对");
        }
        if (isChecker2(pickTaskD, checkInfoDTO, nickName, itemByBarCode)) {
            pickTaskD.setCheckerName2(nickName);
            pickTaskD.setChecker2Qty(qty);
        } else {
            String checker2 = pickTaskD.getCheckerName2();
            if (nickName.equals(checker2)) {
                log.error("不能同一个人员操作");
                throw new BizException("不能同一个人员操作");
            }
            pickTaskD.setCheckQty(qty);
            pickTaskD.setCheckerName(nickName);
        }
    }

    private boolean isUserHaveAuthority(String workerCode, String itemCategoryCode, String stationCode, String warehouseId) {
        WorkgroupVO workgroupVO = new WorkgroupVO();
        workgroupVO.setItemCategoryCode(itemCategoryCode);
        workgroupVO.setStationCode(stationCode);
        workgroupVO.setWorkercode(workerCode);
        controlResource.checkerAuthorityCheck(workgroupVO);

        return true;
    }

    private boolean isChecker2(
            PickTaskD pickTaskD,
            CheckInfoDTO checkInfoDTO,
            String checkerName,
            ItemBarCodeVO itemByBarCode
    ) {
        String workerCode = checkInfoDTO.getWorkerCode();
        String warehouseId = checkInfoDTO.getWarehouseId();
        String stationCode = checkInfoDTO.getStationCode();
        Integer needDoubleCheck = itemByBarCode.getItemHealthIsDouble();
        if (needDoubleCheck == null) {
            throw new BizException("商品资料异常，是否双人复核字段为空");
        }
        if (needDoubleCheck != 1) {
            return false;
        }

        Integer isDoubleCheck = checkInfoDTO.getIsDoubleCheck();
        if (isDoubleCheck == 0) {
            return false;
        }

        String itemCategoryCode = itemByBarCode.getItemCategoryCode();
        boolean haveAuthority = isUserHaveAuthority(workerCode, itemCategoryCode, stationCode, warehouseId);

        if (haveAuthority) {
            String checker1 = pickTaskD.getCheckerName();
            if (checkerName.equals(checker1)) {
                log.error("复核员2与复核员1名称相同");
                throw new BizException("复核员2与复核员1名称相同");
            }
            return true;
        }
        log.error("该复核员没有权限复核改商品" + workerCode);
        throw new BizException("复核员没有权限复核该商品");
    }

    // 获得默认的拼箱
    private BarcodeHDO getDefaultLclNo(String stationCode, String billGroupId, String containerCode, PickTaskD pickTaskD) {
        if (pickTaskD != null && pickTaskD.getPickTaskType() == 0) {
            // 整件生成新的拼箱
            return lclFunc(stationCode, billGroupId, containerCode, pickTaskD, 0);
        }
        List<BarcodeHDO> lclNo = barcodeHMapper.getLclNo(stationCode, billGroupId, '0', null);
        List<BarcodeHDO> List = lclNo.stream().filter(x -> x.getLclNo() != 0).collect(Collectors.toList());
        if (List.isEmpty()) {
            log.info("找不到合适的拼箱，create one");
            return lclFunc(stationCode, billGroupId, containerCode, pickTaskD, 1);
        } else {
            return List.get(0);
        }
    }

    public void updateLcl(
            String barcodeHId,
            String itemCode,
            String itemId,
            BigDecimal qty,
            String containerCode,
            String pickTaskDId,
            Integer isPiece,
            String warehouseId
    ) {
        List<BarCodeDDO> dList = barcodeDMapper.findByMap(MapUtils.put("wmsPickTaskDId", pickTaskDId).put("barcodeId", barcodeHId).
                put("containerCode", containerCode).getMap(), BarCodeDDO.class);
        if (dList.size() > 0) {
        } else {
            //如果没有明细则新增
            BarCodeDDO barCodeDDO = new BarCodeDDO();
            barCodeDDO.setBarcodeId(barcodeHId);
            barCodeDDO.setWmsPickTaskDId(pickTaskDId);
            barCodeDDO.setContainerCode(containerCode);
            barCodeDDO.setId(String.valueOf(SnowFlakeUtils.getId(dataCenterId, machineId)));
            barCodeDDO.setEnterpriseId(userTemplate.getUserDTO().getEnterpriseId());
            barCodeDDO.setWarehouseId(warehouseId);
            barCodeDDO.setItemId(itemId);
            barCodeDDO.setItemCode(itemCode);
            barCodeDDO.setQty(qty);
            barCodeDDO.setIsPiece(isPiece != null && isPiece == 2 ? 1 : isPiece);
            barCodeDDO.setCheckStatus("0");
            barcodeDMapper.save(barCodeDDO);
        }
    }

    @FuncImpl(name = "增加箱子", func = LCLFunc.class, description = "增加箱子")
    public BarcodeHDO lclFunc(String stationCode, String billGroupId, String containerCode, PickTaskD pickTaskD, Integer isPiece) {
        AtomicInteger startNum = new AtomicInteger();
        AtomicReference<BarcodeHDO> lclInDB = new AtomicReference<>();
        try {
            distributedLock.execute(billGroupId, 3000, () -> {
                if ((pickTaskD != null && pickTaskD.getPickTaskType() == 0) || (isPiece != null && isPiece == 0)) {
                    // 整件，统一设置拼箱号为0
                    lclInDB.set(createLclInDB(stationCode, billGroupId, 0, containerCode, pickTaskD, isPiece));
                } else {
                    List<BarcodeHDO> lclNo = barcodeHMapper.getLclNo(null, billGroupId, null, null);
                    if (lclNo.isEmpty()) {
                        startNum.set(1);
                    } else {
                        startNum.set(Optional.ofNullable(lclNo.get(0).getLclNo()).orElse(0) + 1);
                    }
                    lclInDB.set(createLclInDB(stationCode, billGroupId, startNum.get(), containerCode, null, isPiece));
                }
                return true;
            });
        } catch (Exception e) {
            String msg = StringUtils.isBlank(e.getMessage()) ? "执行分布式锁失败" : e.getMessage();
            log.error(msg, e);
            throw new BizException(msg, e);
        }
        return lclInDB.get();
    }

    private BarcodeHDO createLclInDB(
            String stationCode,
            String billGroupId,
            Integer startNum,
            String containerCode,
            PickTaskD pickTaskD,
            Integer isPiece
    ) {
        Criteria criteria = Criteria.forClass(PickTaskH.class);
        criteria.restrictionBuilder().eq("billGroupId", billGroupId).eq("containerCode", containerCode).ne("status", "10");
        criteria.buildRestriction();
        List<PickTaskH> pickTaskHS = pickTaskHMapper.findByCriteria(criteria);
        if (pickTaskHS.size() != 1) {
            throw new BizException("找不到对应的容器任务");
        }
        PickTaskH pickTaskH = pickTaskHS.get(0);
        BarcodeHDO barCodeHDO = new BarcodeHDO();
        barCodeHDO.setId(String.valueOf(SnowFlakeUtils.getId()));
        barCodeHDO.setWarehouseId(pickTaskH.getWarehouseId());
        barCodeHDO.setBldgId(pickTaskH.getBldgId());
        String bldgName = pickTaskH.getBldgName();
        barCodeHDO.setBldgName(bldgName);
        UserDTO userDTO = userTemplate.getUserDTO();
        ModelUtils.setBaseInfo(barCodeHDO, ModelUtils.OpType.INSERT, userDTO);
        barCodeHDO.setLclNo(startNum);
        if (pickTaskH.getPickTaskType() == 3 && startNum == null) {
            barCodeHDO.setLclNo(0);
        } else {
            barCodeHDO.setLclNo(startNum);
        }
        String skidWayCode = "";
        List<WaveBillDO> billDOList = billMapper.findByMap(MapUtils.put("id", pickTaskH.getBillId()).getMap(), WaveBillDO.class);
        if (billDOList != null && billDOList.size() > 0) {
            OmsDoBillHDO omsDoBillHDO = omsDoBillHMapper.findById(billDOList.get(0).getSrcId(), OmsDoBillHDO.class);
            if (omsDoBillHDO != null && !StringUtils.isBlank(omsDoBillHDO.getLineCode())) {
                RestMessage<List<BaseCommonDict>> rest = commonClient.getDictBasicListByCodes("common_shiplineid",
                        new String[]{omsDoBillHDO.getLineCode()});
                if (rest.isSuccess() && rest.getData() != null && rest.getData().size() > 0 && rest.getData().get(0) != null) {
                    skidWayCode = rest.getData().get(0).getDictDesc();
                }
            }
        }
        if (pickTaskH.getPickTaskType() == 3 && pickTaskD != null && pickTaskD.getPickTaskType() == 0) {
            barCodeHDO.setBarcode(StringUtils.isBlank(skidWayCode) ? pickTaskD.getPickTaskDId() : skidWayCode + pickTaskD.getPickTaskDId());
        } else {
            long id = SnowFlakeUtils.getId(dataCenterId, machineId);
            barCodeHDO.setBarcode(StringUtils.isBlank(skidWayCode) ? String.valueOf(id) : skidWayCode + id);
        }
        //散货箱
        if (isPiece == null) {
            barCodeHDO.setType("0");
        } else if (isPiece == 0) {
            barCodeHDO.setType("1");
        } else if (isPiece == 1) {
            barCodeHDO.setType("0");
        }
        barCodeHDO.setStationCode(stationCode);
        barCodeHDO.setContainerCode(containerCode);
        barCodeHDO.setBillGroupId(billGroupId);
        barCodeHDO.setBarcode(String.valueOf(SnowFlakeUtils.getId(dataCenterId, machineId)));
        //未使用状态
        barCodeHDO.setStatus('0');
        barCodeHDO.setCheckStatus("-1");
        barcodeHMapper.save(barCodeHDO);
        return barCodeHDO;
    }

    // 内复核索取
    public InnerCheckBillV2VO findPickupByContainerFuncV2(
            String containerCode,
            String stationCode,
            List<CheckPickTaskDDTO> checkPickTaskDDTOList,
            String billGroupId,
            Integer lclNo,
            Integer isDoubleCheck,
            String warehouseId
    ) {
        Criteria crt = Criteria.forClass(PickTaskH.class); // 查询未复核完成的容器
        Restriction r1 = Restrictions.in("status", new String[]{"0", "1", "2", "3"});
        Restriction r2 = Restrictions.eq("containerCode", containerCode);
        // Restriction r3 = Restrictions.isnull("stationCode"); 鹰江 拣货确认后未分配复核台，此处查询无需复核台
        Restriction r4 = Restrictions.eq("warehouseId", warehouseId);
        // 使用or实现，业务设计，只能查出一个pick_task_h
        crt.setRestriction(Restrictions.and(r1, r2, r4));
        List<PickTaskH> pckTaskHs = pickTaskHMapper.findByCriteria(crt);
        if (pckTaskHs.isEmpty()) {
            throw new BizException("找不到对应容器任务");
        } else if (pckTaskHs.size() > 1) {
            throw new BizException("容器绑定错误，该容器绑定了多个未完成任务");
        }
        PickTaskH pckTaskH = pckTaskHs.get(0);
        if (StrUtil.isNotEmpty(pckTaskH.getStationCode()) && !"-1".equals(pckTaskH.getStationCode())) {
            if (!pckTaskH.getStationCode().equals(stationCode)) {
                throw new BizException("当前周转箱已在" + pckTaskH.getStationName() + "进行复核");
            }
            if (Integer.parseInt(pckTaskH.getStatus()) < 2) {
                throw new BizException("当前周转箱未拣货完成！");
            }
        } else {
            // 查询复核台
            WhWorkstationQueryVO queryVO = new WhWorkstationQueryVO();
            queryVO.setCode(stationCode);
            queryVO.setEnterpriseId(pckTaskH.getEnterpriseId());
            queryVO.setWarehouseId(pckTaskH.getWarehouseId());
            RestMessage<Page<BaseWmsWhWorkstation>> page = warehouseResource.getPage(queryVO);
            if (!page.isSuccess() || page.getData() == null || CollUtil.isEmpty(page.getData().getList())) {
                throw new BizException("复核台错误！");
            }
            BaseWmsWhWorkstation vo = page.getData().getList().stream().filter(t -> t.getStationCode().equals(stationCode)).findFirst().orElse(null);
            if (null == vo) {
                throw new BizException("复核台错误！");
            }
            pickTaskHMapper.updateMapById(pckTaskH.getPickTaskId(), MapUtils.put("stationId", vo.getStationId()).put("stationCode", vo.getStationCode()).put("stationName", vo.getStationName()).getMap(), PickTaskH.class);
        }
        String pickTaskHId = pckTaskH.getPickTaskId();
        // 查询当前任务下的明细
        List<InnerCheckTaskDV2VO> dBInnerCheckTaskDVOs = pickTaskDMapper.getPickTaskdAndWareHouseV2(pickTaskHId, warehouseId);
        //校验pick_task_d是否已经拣货完成
        for (InnerCheckTaskDV2VO dBInnerCheckTaskDVO : dBInnerCheckTaskDVOs) {
            if (dBInnerCheckTaskDVO.getPickQty() == null || (null != dBInnerCheckTaskDVO.getFlowStatus() && Integer.parseInt(dBInnerCheckTaskDVO.getFlowStatus()) < 3)) {
                //查询pick_order获取索取人信息和拣货单号
                PickOrder pickOrder = pickOrderMapper.getPickOrderByPickTaskDId(dBInnerCheckTaskDVO.getPickTaskDId());
                String pickName = "";
                String pickOrderId = "";
                if (null != pickOrder) {
                    pickOrderId = pickOrder.getPickOrderId();
                    pickName = pickOrder.getOperatorName();
                }
                throw new BizException("当前周转箱内的商品还未拣货确认,拣货人【" + pickName + "】, 拣货单号为【" + pickOrderId + "】, 请拣货确认后再次扫描周转进行内复核操作");
            }
        }
        Criteria redCrt = Criteria.forClass(SaleRedRecord.class);
        redCrt.restrictionBuilder().in("pickTaskDId", dBInnerCheckTaskDVOs.stream().map(InnerCheckTaskDV2VO::getPickTaskDId).distinct().toArray());
        redCrt.buildRestriction();
        List<SaleRedRecord> saleRedRecords = saleRedRecordMapper.findByCriteria(redCrt);
        dBInnerCheckTaskDVOs = dBInnerCheckTaskDVOs.stream().filter(x -> x.getPickQty() != null).collect(Collectors.toList());
        // 从redis获取缓存信息
        String redisKey = "wms:innerCheck:" + pickTaskHId;
        Map redisMap = (Map) redisTemplate.opsForValue().get(redisKey);
        InnerCheckBillV2VO innerCheckBillVO = new InnerCheckBillV2VO();
        // innerCheckBillVO.setStationCode(stationCode);
        List<BaseMtlItemMst> itemList = null;
        if (null != redisMap) {
            innerCheckBillVO = JSON.parseObject(redisMap.get("vo").toString(), InnerCheckBillV2VO.class);
            itemList = JSON.parseArray(redisMap.get("items").toString(), BaseMtlItemMst.class);
            for (InnerCheckTaskDV2VO dBInnerCheckTaskDV2VO : dBInnerCheckTaskDVOs) {
                String tempPickTaskDId = dBInnerCheckTaskDV2VO.getPickTaskDId();
                InnerCheckTaskDV2VO redisInnerCheckTaskDV2VO = innerCheckBillVO.getInnerCheckTaskDVOS()
                        .stream().filter(t -> t.getPickTaskDId().equals(tempPickTaskDId)).findFirst().orElse(null);
                if (redisInnerCheckTaskDV2VO != null) {
                    dBInnerCheckTaskDV2VO.setIsSupervise(redisInnerCheckTaskDV2VO.getIsSupervise());
                    dBInnerCheckTaskDV2VO.setNoSuperviseReason(redisInnerCheckTaskDV2VO.getNoSuperviseReason());
                    dBInnerCheckTaskDV2VO.setSalespersonName(redisInnerCheckTaskDV2VO.getSalespersonName());
                    dBInnerCheckTaskDV2VO.setPaymentType(redisInnerCheckTaskDV2VO.getPaymentType());
                    dBInnerCheckTaskDV2VO.setCreateby(redisInnerCheckTaskDV2VO.getCreateby());
                    dBInnerCheckTaskDV2VO.setRemark(redisInnerCheckTaskDV2VO.getRemark());
                    dBInnerCheckTaskDV2VO.setShipperId(redisInnerCheckTaskDV2VO.getShipperId());
                    dBInnerCheckTaskDV2VO.setConsignorId(redisInnerCheckTaskDV2VO.getConsignorId());
                    dBInnerCheckTaskDV2VO.setBillCode(redisInnerCheckTaskDV2VO.getBillCode());
                    dBInnerCheckTaskDV2VO.setBillType(redisInnerCheckTaskDV2VO.getBillType());
                    dBInnerCheckTaskDV2VO.setCargoOwnerName(redisInnerCheckTaskDV2VO.getCargoOwnerName());
                } else {
                    try {
                        List<InnerCheckTaskDV2VO> tempList = Collections.singletonList(dBInnerCheckTaskDV2VO);
                        RestMessage<List<? extends IsSuperviseDTO>> rest = inBoundClient.isSupervise(tempList, 2);
                        if (rest.isSuccess() && rest.getData() != null && rest.getData().size() == tempList.size()) {
                            for (int i = 0; i < rest.getData().size(); i++) {
                                BeanUtils.copyProperties(rest.getData().get(i), tempList.get(i));
                            }
                        }
                        innerCheckBillVO.getInnerCheckTaskDVOS().addAll(tempList);
                        redisMap.put("vo", JSON.toJSONString(innerCheckBillVO));
                        redisMap.put("items", JSON.toJSONString(itemList));
                        redisTemplate.opsForValue().set(redisKey, redisMap, 60, TimeUnit.MINUTES);
                    } catch (Exception e) {
                        log.error("调用监管码接口异常", e);
                    }
                }
                // 给dBInnerCheckTaskDV2VO设置商品属性值
                extractedItem(itemList, dBInnerCheckTaskDV2VO);
            }
        } else {
            innerCheckBillVO.setWarehouseId(pckTaskH.getWarehouseId());
            innerCheckBillVO.setPickTaskHId(pickTaskHId);
            innerCheckBillVO.setBillGroupId(pckTaskH.getBillGroupId());
            innerCheckBillVO.setContainerCode(containerCode);
            innerCheckBillVO.setStationCode(stationCode);
            // 查询波次订单信息用来获取收货人信息
            Criteria cr = Criteria.forClass(WaveBillDO.class);
            Restriction restriction = Restrictions.in("id", dBInnerCheckTaskDVOs.stream().map(InnerCheckTaskDV2VO::getBillId).distinct().collect(Collectors.toList()));
            cr.setRestriction(restriction);
            List<WaveBillDO> billHList = billMapper.findByCriteria(cr);
            if (CollectionUtil.isNotEmpty(billHList)) {
                WaveBillDO waveBillDO = billHList.get(0);
                innerCheckBillVO.setShipperId(waveBillDO.getShipperId());
                innerCheckBillVO.setShipperName(waveBillDO.getShipperName());
                innerCheckBillVO.setConsignorId(waveBillDO.getConsignorId());
                innerCheckBillVO.setConsignorName(waveBillDO.getConsignorName());
                innerCheckBillVO.setBillCode(waveBillDO.getSrcCode());
                innerCheckBillVO.setBillType(waveBillDO.getType());
            }
            // 根据id查询商品批次信息，用来获取有效期
            Set<String> itemIdSet = dBInnerCheckTaskDVOs.stream().map(InnerCheckTaskDV2VO::getItemId).collect(Collectors.toSet());
            if (itemIdSet.size() > 0 && !StringUtils.isBlank(itemIdSet.iterator().next())) {
                itemList = itemClient.getListByIds(itemIdSet).getData();
            }
            for (InnerCheckTaskDV2VO innerCheckTaskDVO : dBInnerCheckTaskDVOs) {
                if (CollectionUtil.isNotEmpty(billHList)) {
                    WaveBillDO bill = billHList.stream().filter(t -> t.getId().equals(innerCheckTaskDVO.getBillId())).findFirst().orElse(null);
                    if (null != bill) {
                        innerCheckTaskDVO.setSalespersonName(bill.getSalesPersonName());
                        innerCheckTaskDVO.setPaymentType(bill.getPaymentDayType());
                        innerCheckTaskDVO.setCreateby(bill.getCreateBy());
                        innerCheckTaskDVO.setRemark(bill.getRemark());
                        innerCheckTaskDVO.setShipperId(bill.getShipperId());
                        innerCheckTaskDVO.setCreateby(bill.getCreateBy());
                        innerCheckTaskDVO.setCargoOwnerCode(bill.getShipperCode());
                        innerCheckTaskDVO.setConsignorId(bill.getConsignorId());
                        innerCheckTaskDVO.setBillCode(bill.getSrcId());
                        innerCheckTaskDVO.setBillType(bill.getType());
                    }
                }
                // 给innerCheckTaskDVO设置商品属性值
                extractedItem(itemList, innerCheckTaskDVO);
            }
            // 填写是否扫描监管码 阶段：0=入库质检 1=PC拣货 2=PC内复核 3=PC外复核 4=补录 5=PDA拣货 6=PDA内复核 7=PDA外复核
            if (dBInnerCheckTaskDVOs.size() > 0) {
                try {
                    RestMessage<List<? extends IsSuperviseDTO>> rest = inBoundClient.isSupervise(dBInnerCheckTaskDVOs, 2);
                    if (rest.isSuccess() && rest.getData() != null && rest.getData().size() == dBInnerCheckTaskDVOs.size()) {
                        for (int i = 0; i < rest.getData().size(); i++) {
                            BeanUtils.copyProperties(rest.getData().get(i), dBInnerCheckTaskDVOs.get(i));
                        }
                    }
                } catch (Exception e) {
                    log.error("调用监管码接口异常", e);
                }
            }
        }

        innerCheckBillVO.setInnerCheckTaskDVOS(dBInnerCheckTaskDVOs);
        if (CollectionUtil.isNotEmpty(checkPickTaskDDTOList)) {
            List<String> pickTaskDIdList = checkPickTaskDDTOList.stream().map(CheckPickTaskDDTO::getPickTaskDId).collect(Collectors.toList());
            innerCheckBillVO.setCurrentCheckIdList(pickTaskDIdList);
        } else {
            if (!"3".equals(pckTaskH.getStatus())) {
                // 更新 pick_task_h 表status为3=内复核待确认
                pickTaskHMapper.updateMapById(pickTaskHId, MapUtils.put("status", "3").getMap(), PickTaskH.class);
                //更新 pick_task_d 表flowStatus为4=内复核已索取
                updateFlowStatus(pickTaskHId, 4);
            }
        }

        InnerCheckContainerVO containerVO = new InnerCheckContainerVO();
        Set<String> totalItemSet = new HashSet<>();
        Set<String> checkedItemSet = new HashSet<>();
        Set<String> totalLotNoSet = new HashSet<>();
        Set<String> checkedLotNoSet = new HashSet<>();
        BigDecimal redNum = BigDecimal.ZERO;
        BigDecimal totalNum = BigDecimal.ZERO;
        BigDecimal checkedNum = BigDecimal.ZERO;
        Set<String> set = new HashSet<>();
        for (InnerCheckTaskDV2VO innerCheckTaskDVO : dBInnerCheckTaskDVOs) {
            List<SaleRedRecord> subRedList = saleRedRecords.stream().filter(t -> t.getPickTaskDId().equals(innerCheckTaskDVO.getPickTaskDId())).collect(Collectors.toList());
            innerCheckTaskDVO.setRedQty(subRedList.stream().map(SaleRedRecord::getQty).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
            innerCheckTaskDVO.setRedReason(subRedList.stream().map(SaleRedRecord::getReason).filter(reason -> !StringUtils.isBlank(reason)).collect(Collectors.joining("|")));
            // 给innerCheckTaskDVO设置商品属性值
            extractedItem(itemList, innerCheckTaskDVO);
            // 总品种
            totalItemSet.add(innerCheckTaskDVO.getItemId());
            // 已经检查的品种数量
            if (isChecked(innerCheckTaskDVO)) {
                checkedItemSet.add(innerCheckTaskDVO.getItemId());
                if (!StringUtils.isBlank(innerCheckTaskDVO.getLotNo())) {
                    checkedLotNoSet.add(innerCheckTaskDVO.getLotNo());
                }
            }
            // 合计批号
            if (!StringUtils.isBlank(innerCheckTaskDVO.getLotNo())) {
                totalLotNoSet.add(innerCheckTaskDVO.getLotNo());
            }

            if (innerCheckTaskDVO.getRedQty() != null) {
                redNum = redNum.add(innerCheckTaskDVO.getRedQty());
            }

            if (!set.contains(innerCheckTaskDVO.getPickTaskDId())) {
                BigDecimal pickQty = innerCheckTaskDVO.getPickQty();
                totalNum = totalNum.add(pickQty == null ? BigDecimal.ZERO : pickQty);
                if (isChecked(innerCheckTaskDVO)) {
                    if (StringUtils.isBlank(innerCheckTaskDVO.getChecker2())) {
                        checkedNum = checkedNum.add(innerCheckTaskDVO.getCheckQty() == null ? BigDecimal.ZERO : innerCheckTaskDVO.getCheckQty());
                    } else {
                        checkedNum = checkedNum.add(innerCheckTaskDVO.getCheck2Qty() == null ? BigDecimal.ZERO : innerCheckTaskDVO.getCheck2Qty());
                    }
                }
                set.add(innerCheckTaskDVO.getPickTaskDId());
            }
        }
        containerVO.setTotalVarieties(totalItemSet.size());
        containerVO.setCheckedVarieties(checkedItemSet.size());
        containerVO.setUnCheckedVarieties(containerVO.getTotalVarieties() - containerVO.getCheckedVarieties());
        containerVO.setTotalLot(totalLotNoSet.size());
        containerVO.setCheckedLot(checkedLotNoSet.size());
        containerVO.setUnCheckedLot(containerVO.getTotalLot() - containerVO.getCheckedLot());
        BigDecimal unCheckedNum = totalNum.subtract(checkedNum).subtract(redNum);
        containerVO.setUnCheckedNum(unCheckedNum);
        containerVO.setTotalNum(totalNum);
        containerVO.setCheckedNum(checkedNum);
        containerVO.setRedNum(redNum);
        innerCheckBillVO.setContainerInfo(containerVO);
        //查询是否尾箱
        Integer unConfirmContainerCnt = barcodeHMapper.getEndBoxContainer(containerCode, null, pckTaskH.getBillGroupId());
        if (null != unConfirmContainerCnt && unConfirmContainerCnt == 0) {
            innerCheckBillVO.setIsEndBox(1);
        }

        //查询拼箱信息
        innerCheckBillVO.setLclVO(queryLcl(stationCode, pckTaskH.getBillGroupId(), lclNo, containerCode, isDoubleCheck, itemList));
        // 循环设置lvlNo和lvlNum
        for (InnerCheckTaskDV2VO innerCheckTaskDVO : innerCheckBillVO.getInnerCheckTaskDVOS()) {
            if (null != innerCheckBillVO.getLclVO() && CollUtil.isNotEmpty(innerCheckBillVO.getLclVO().getLcls())) {
                for (BarCodeHVO barCodeHVO : innerCheckBillVO.getLclVO().getLcls()) {
                    if (CollUtil.isNotEmpty(barCodeHVO.getBarcodeDList())) {
                        for (BarCodeDVO barCodeDVO : barCodeHVO.getBarcodeDList()) {
                            if (innerCheckTaskDVO.getPickTaskDId().equals(barCodeDVO.getPickTaskDId())) {
                                innerCheckTaskDVO.setLclNo(String.valueOf(barCodeHVO.getLclNo()));
                                innerCheckTaskDVO.setLclQty(barCodeDVO.getCheckQty());
                                innerCheckTaskDVO.setBarcodeDId(barCodeDVO.getBarcodeDId());
                            }
                        }
                    }
                }
            }
        }
        if (null == redisMap) {
            Map<String, String> map = new HashMap<>();
            map.put("vo", JSON.toJSONString(innerCheckBillVO));
            map.put("items", JSON.toJSONString(itemList));
            redisTemplate.opsForValue().set(redisKey, map, 60, TimeUnit.MINUTES);
        }

        if (CollectionUtil.isNotEmpty(checkPickTaskDDTOList)) {
            // 计算当前复核明细Id集合数据
            InnerCurrentCheckVO innerCurrentCheckVO = new InnerCurrentCheckVO();
            List<InnerCurrentCheckVO.LotAndNum> lotAndNumList = Lists.newArrayList();
            List<InnerCheckTaskDV2VO> innerCheckTaskDVOS = innerCheckBillVO.getInnerCheckTaskDVOS();
            List<String> currentTaskIds = checkPickTaskDDTOList.stream().map(CheckPickTaskDDTO::getPickTaskDId).collect(Collectors.toList());
            List<InnerCheckTaskDV2VO> currentTasks = innerCheckTaskDVOS.stream().filter(t -> currentTaskIds.contains(t.getPickTaskDId())).collect(Collectors.toList());

            Map<String, List<InnerCheckTaskDV2VO>> collectMap = currentTasks.stream().collect(Collectors.groupingBy(InnerCheckTaskDV2VO::getLotNo));
            for (String lotNo : collectMap.keySet()) {
                InnerCurrentCheckVO.LotAndNum lotAndNum = innerCurrentCheckVO.new LotAndNum();
                List<InnerCheckTaskDV2VO> innerCheckTaskDV2VOS = collectMap.get(lotNo);
                List<InnerCheckTaskDV2VO> redTaskVos = innerCheckTaskDV2VOS.stream().filter(t -> t.getRedQty() != null).collect(Collectors.toList());
                BigDecimal batchRedQty = CollectionUtil.isEmpty(redTaskVos) ? BigDecimal.ZERO : redTaskVos.stream().map(InnerCheckTaskDV2VO::getRedQty).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal num = innerCheckTaskDV2VOS.stream().map(InnerCheckTaskDV2VO::getPickQty).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
                lotAndNum.setLotNo(lotNo);
                lotAndNum.setNum(num.subtract(batchRedQty));
                lotAndNumList.add(lotAndNum);
            }

            BigDecimal checkQty = currentTasks.stream().map(InnerCheckTaskDV2VO::getCheckQty).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal pickQty = currentTasks.stream().map(InnerCheckTaskDV2VO::getPickQty).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal qty = currentTasks.stream().map(InnerCheckTaskDV2VO::getQty).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
            innerCurrentCheckVO.setLotAndNumList(lotAndNumList);
            innerCurrentCheckVO.setCheckQty(qty.subtract(redNum));
            // innerCurrentCheckVO.setCheckQty(checkQty);
            innerCurrentCheckVO.setPickQty(pickQty);
            innerCurrentCheckVO.setQty(qty);
            innerCurrentCheckVO.setPickTaskDId(currentTaskIds);
            innerCheckBillVO.setCurrentCheckInfoVo(innerCurrentCheckVO);
        }
        return innerCheckBillVO;
    }

    @Override
    @Transactional
    public List<BarCodeDVO> queryLclByBarcodeId(String barcodeId) {
        AssertUtils.isNotBlank(barcodeId, "参数不能为空");

        List<BarCodeDDO> barCodeDDOS = barcodeDMapper.findByMap(MapUtils.put("barcodeId", barcodeId).getMap(),
                BarCodeDDO.class);
        if (CollectionUtil.isEmpty(barCodeDDOS)) {
            return null;
        }
        List<BarCodeDVO> barCodeDVOs = barCodeDDOS.stream().filter(p -> p.getQty() != null && p.getQty().compareTo(BigDecimal.ZERO) > 0).
                map(barCodeDDO -> {
                    BarCodeDVO barCodeDVO = new BarCodeDVO();
                    barCodeDVO.setBarcodeDId(barCodeDDO.getId());
                    barCodeDVO.setPickTaskDId(barCodeDDO.getWmsPickTaskDId());
                    barCodeDVO.setContainerCode(barCodeDDO.getContainerCode());
                    BeanUtils.copyProperties(barCodeDDO, barCodeDVO);
                    //barCodeDVO.setPrintCount(lcl.getPrintCount());
                    return barCodeDVO;
                }).collect(Collectors.toList());


        PickTaskD pickTaskD = pickTaskDMapper.findById(barCodeDVOs.get(0).getPickTaskDId(), PickTaskD.class);
        AssertUtils.isNotNull(pickTaskD, "查询拣选任务异常，拣选明细单号：" + barCodeDVOs.get(0).getPickTaskDId());
        String pickTaskHId = pickTaskD.getPickTaskId();
        String redisKey = "wms:innerCheck:" + pickTaskHId;
        Map redisMap = (Map) redisTemplate.opsForValue().get(redisKey);
        List<InnerCheckTaskDV2VO> dBInnerCheckTaskDVOs = pickTaskDMapper.getPickTaskdAndWareHouseV2(pickTaskHId, null);
        Map<String, List<InnerCheckTaskDV2VO>> collect = dBInnerCheckTaskDVOs.stream().collect(Collectors.groupingBy(InnerCheckTaskDV2VO::getPickTaskDId));
        List<BaseMtlItemMst> itemList = null;
        if (redisMap != null) {
            itemList = JSON.parseArray(redisMap.get("items").toString(), BaseMtlItemMst.class);
        } else {
            Set<String> itemIdSet = dBInnerCheckTaskDVOs.stream().map(t -> t.getItemId()).collect(Collectors.toSet());
            if (itemIdSet != null && itemIdSet.size() > 0 && !StringUtils.isBlank(itemIdSet.iterator().next())) {
                itemList = itemClient.getListByIds(itemIdSet).getData();
            }
        }
        Map<String, BaseMtlItemMst> itemMap = null;
        if (CollectionUtil.isNotEmpty(itemList)) {
            itemMap = itemList.stream().collect(Collectors.toMap(BaseMtlItemMst::getItemId, Function.identity()));
        }


        for (int i = 0; i < barCodeDVOs.size(); i++) {
            BarCodeDVO barCodeDVO = barCodeDVOs.get(i);
            barCodeDVO.setBatchNum(collect.get(barCodeDVO.getPickTaskDId()) == null ? "" : collect.get(barCodeDVO.getPickTaskDId()).get(0).getBatchNum());
            if (CollectionUtil.isNotEmpty(itemMap)) {
                BaseMtlItemMst baseMtlItemMst = itemMap.get(barCodeDVO.getItemId());
                barCodeDVO.setItemName(baseMtlItemMst == null ? "" : baseMtlItemMst.getItemName());
                List<BaseMtlItemUomConversions> uomList = baseMtlItemMst.getBaseMtlItemUomConversionsList();
                if (uomList != null && uomList.size() > 0) {
                    barCodeDVO.setCaseConversionRate(uomList.get(0).getConversionRate());
                }
            }
        }
        return barCodeDVOs;
    }


    @Override
    @Transactional
    public RestMessage<Boolean> lclAdjust(List<LclAdjustDTO> lclInfos) {
        for (LclAdjustDTO adjustInfo : lclInfos) {
            if (adjustInfo.getQty() == null || adjustInfo.getQty().compareTo(BigDecimal.ZERO) < 0) {
                throw new BizException("数量必须大于0");
            }
            Integer lclNo = adjustInfo.getLclNo();
            AssertUtils.isNotNull(lclNo);
            String containerCode = adjustInfo.getContainerCode();
            String billGroupId = adjustInfo.getBillGroupId();
            AssertUtils.isNotBlank(billGroupId);
            Map<String, Object> map = MapUtils.put("billGroupId", billGroupId).put("lclNo", lclNo).getMap();

            List<BarcodeHDO> barcodeHs = barcodeHMapper.findByMap(map, BarcodeHDO.class);
            if (barcodeHs.isEmpty()) {
                throw new BizException("找不到对应的拼箱 ");
            }

            if (barcodeHs.size() > 1) {
                throw new BizException("找到多个拼箱");
            }

            String itemCode = adjustInfo.getItemCode();
            BigDecimal qty = adjustInfo.getQty();
            BarcodeHDO barcodeHDO = barcodeHs.get(0);
            String barcodeId = barcodeHDO.getId();
            String itemId = adjustInfo.getItemId();
            String pickTaskDId = adjustInfo.getPickTaskDId();
            updateLcl(barcodeId, itemCode, itemId, qty, containerCode, pickTaskDId, 1, barcodeHDO.getWarehouseId());
        }

        return RestMessage.doSuccess(true);
    }


    private List<InnerCheckTaskDV2VO> sortInnerCheckTaskDV2VOS(List<CheckPickTaskDDTO> checkPickTaskDDTOList, List<InnerCheckTaskDV2VO> innerCheckTaskDVOs) {
        //正在复核的排最前面
        if (CollectionUtil.isEmpty(checkPickTaskDDTOList)) {
            return innerCheckTaskDVOs;
        }
        List<String> currentTaskIds = checkPickTaskDDTOList.stream().map(c -> c.getPickTaskDId()).collect(Collectors.toList());
        List<InnerCheckTaskDV2VO> innerCheckTaskDV2Result = Lists.newArrayList();
        List<InnerCheckTaskDV2VO> headList = Lists.newArrayList();
        Iterator<InnerCheckTaskDV2VO> iterator = innerCheckTaskDVOs.iterator();
        while (iterator.hasNext()) {
            InnerCheckTaskDV2VO next = iterator.next();
            if (currentTaskIds.contains(next.getPickTaskDId())) {
                headList.add(next);
                iterator.remove();
            }
        }
        innerCheckTaskDV2Result.addAll(headList);
        innerCheckTaskDV2Result.addAll(innerCheckTaskDVOs);
        return innerCheckTaskDV2Result;
    }

    @Override
    @Transactional
    @Flow(name = "打印拼箱", category = "出库/复核管理/内复核/打印", index = 3, description = "1、校验数据 2、查询获取拼箱数据 3、更新打印次数+1")
    public PrintData printLcl(String containerCode, String billGroupId, String lclNo) {
        //查询拼箱
        MapToolKit billGroupIdCond = MapUtils.put("billGroupId", billGroupId);
        List<BarcodeHDO> barcodeHDOs = barcodeHMapper.findByMap(billGroupIdCond.put("lclNo", lclNo).getMap(), BarcodeHDO.class);
        AssertUtils.isTrue(barcodeHDOs.size() == 1, "拼箱号必须唯一存在");
        if (barcodeHDOs.get(0).getStatus() == '0') {
            List<BarCodeDDO> barCodeDDOList = barcodeDMapper.findByMap(Collections.singletonMap("barcodeId", barcodeHDOs.get(0).getId()), BarCodeDDO.class);
            if (CollUtil.isEmpty(barCodeDDOList)) {
                throw new BizException("当前拼箱内无商品，不可封箱打印！");
            }
            barcodeHMapper.updateStatuts("1", barcodeHDOs.stream().map(BarcodeHDO::getId).distinct().collect(Collectors.toList()));
        }
        return new PrintData();
    }

    @Override
    @Transactional
    @Flow(name = "打印拼箱", category = "出库/复核管理/内复核/打印", index = 3, description = "1、校验数据 2、查询获取拼箱数据 3、更新打印次数+1")
    public PrintData printLclDetail(String containerCode, String billGroupId, String lclNo) {
        List<WaveBillDO> billDOs = validPrintBoxFunc.validPrintLclFunc(containerCode, billGroupId, lclNo);
        return printLclFunc.PrintLclFunc(containerCode, billGroupId, lclNo, billDOs);
    }

    @FuncImpl(name = "拼箱打印校验", func = ValidPrintBoxFunc.class, description = "拼箱打印校验")
    public List<WaveBillDO> validPrintLclFunc(String containerCode, String billGroupId, String lclNo) {
        if (StringUtils.isBlank(containerCode) || StringUtils.isBlank(billGroupId) || StringUtils.isBlank(lclNo))
            throw new ParameterException("容器号/订单编号/拼箱编号不能为空！");

        List<WaveBillDO> billDOs = billMapper.findByMap(MapUtils.put("billGroupId", billGroupId).getMap(), WaveBillDO.class);
        if (billDOs.size() < 1) {
            log.error("找不到对应的订单");
            throw new BizException("找不到对应的订单");
        }
        return billDOs;
    }

    @FuncImpl(name = "打印拼箱", func = PrintLclFunc.class, description = "打印拼箱")
    public PrintData printLclFunc(String containerCode, String billGroupId, String lclNo, List<WaveBillDO> billDOs) {
        MapToolKit billGroupIdCond = MapUtils.put("billGroupId", billGroupId);
        WaveBillDO billDO = billDOs.get(0);
        Map<String, Object> map = MapUtils.put("billid", billDO.getSrcId()).getMap();
        List<OmsDoBillHDO> omsDoBillHDOS = omsDoBillHMapper.findByMap(map, OmsDoBillHDO.class);
        if (omsDoBillHDOS.size() < 1) {
            log.error("找不到对应的oms订单");
            throw new BizException("找不到对应的oms订单");
        }
        OmsDoBillHDO omsDoBillHDO = omsDoBillHDOS.get(0);
        List<BarcodeHDO> barcodeHDOs = barcodeHMapper.findByMap(billGroupIdCond.put("lclNo", lclNo).getMap(), BarcodeHDO.class);
        AssertUtils.isTrue(barcodeHDOs.size() == 1, "拼箱号必须唯一存在");
        BarcodeHDO barcodeHDO = barcodeHDOs.get(0);
        if (barcodeHDO.getStatus() == '0') {
            //封箱
            barcodeHDO.setPrintCount(barcodeHDO.getPrintCount() == null ? 0 : barcodeHDO.getPrintCount() + 1);
            barcodeHDO.setStatus('1');
            barcodeHMapper.update(barcodeHDO);
        }
        List<BarCodeDDO> barCodeDDOList = barcodeDMapper.findByMap(Collections.singletonMap("barcodeId", barcodeHDO.getId()), BarCodeDDO.class);
        if (CollUtil.isEmpty(barCodeDDOList)) {
            throw new BizException("当前拼箱内无商品，不可封箱打印！");
        }
        BarCodeDDO barCodeDDO = barCodeDDOList.get(0);
        Integer printCnt = barCodeDDO.getPrintCnt();
        barcodeDMapper.updateMapById(barCodeDDO.getId(), Collections.singletonMap("printCnt", null == printCnt ? 1 : printCnt + 1), BarCodeDDO.class);
//        Set<String> itemIds = barCodeDDOList.stream().map(BarCodeDDO::getItemId).collect(Collectors.toSet());
        //根据barcodeDId获取pickTaskD
        List<InnerCheckTaskDV2VO> pickTaskDList = pickTaskDMapper.listPickTaskDByBarcodeDId(Collections.singletonList(barCodeDDO.getId()));
        Set<String> itemIds = pickTaskDList.stream().map(InnerCheckTaskDV2VO::getItemId).collect(Collectors.toSet());
        RestMessage<List<BaseMtlItemMst>> itemMsg = itemClient.getListByIds(itemIds);
        Map<String, String> itemInfo = new HashMap<>();
        itemMsg.getData().forEach(var -> itemInfo.put(var.getItemId(), var.getItemName()));
        Map<String, Object> head = new HashMap<>();
        head.put("barcode", barCodeDDO.getId());
        head.put("lclNo", DateUtil.format(barcodeHDO.getGmtCreate(), DatePattern.PURE_DATE_PATTERN) + "拼" + barcodeHDO.getLclNo());
        List<WorkStageRuntimeDO> workStages = workStageRuntimeMapper.findByMap(billGroupIdCond.getMap(), WorkStageRuntimeDO.class);
        String workStageCodes = workStages.stream().
                map(WorkStageRuntimeDO::getStageCode).
                filter(s -> !StringUtils.isBlank(s)).
                distinct().collect(Collectors.joining(","));
        head.put("zcq", workStageCodes);
        head.put("psfx", omsDoBillHDO.getLineName());
        head.put("billno", omsDoBillHDO.getBillno());
        head.put("CPH", (printCnt != null && printCnt >= 1) ? "重" : "");
        //客户编号
        head.put("shipperCode", omsDoBillHDO.getShipperCode());
        head.put("shipperName", omsDoBillHDO.getShippername());

        //运输路线
        RestMessage<List<BaseCommonDict>> shiplineidMsg = commonClient.getDictBasicListByCodes("common_shiplineid", null);
        if (shiplineidMsg.isSuccess() && CollUtil.isNotEmpty(shiplineidMsg.getData())) {
            BaseCommonDict dict = shiplineidMsg.getData().stream().filter(var -> var.getDictCode().equals(omsDoBillHDO.getShipLineId())).findFirst().orElse(null);
            if (null == dict) {
                head.put("psfx", omsDoBillHDO.getLineName());
            } else {
                head.put("psfx", dict.getDictName());
            }
        }
        //复核台
        head.put("fht", barcodeHDO.getStationCode());
        //收货地址
        head.put("receiveAddress", omsDoBillHDO.getReceiveAddress());
        //复核人
        String checkName = StrUtil.isEmpty(pickTaskDList.get(0).getChecker1()) ? pickTaskDList.get(0).getChecker2() : pickTaskDList.get(0).getChecker1();
        head.put("checkName", StrUtil.isEmpty(checkName) ? userTemplate.getUserDTO().getNickname() : checkName);
        //联系人
        head.put("lxr", omsDoBillHDO.getCustPerson());
        //电话
        head.put("tel", omsDoBillHDO.getCustPersonTel());
        //备注
        head.put("remark", omsDoBillHDO.getRemark());
        //开票员
        head.put("kpy", omsDoBillHDO.getSalespersonName());
        //最大20行
        int maxLines = 20;
        List<ReportData> datas = new ArrayList<>();
        for (int i = 0; i < pickTaskDList.size() / 20 + 1; i++) {
            int subIndex = Math.min(maxLines, pickTaskDList.size() - maxLines * i);
            List<InnerCheckTaskDV2VO> subList = pickTaskDList.subList(i * maxLines, subIndex);
            List<Map<String, Object>> table = new ArrayList<>();
            ReportData data = new ReportData();
            for (InnerCheckTaskDV2VO taskDV2VO : subList) {
                Map<String, Object> tableDetail = new HashMap<>();
                tableDetail.put("itemname", itemInfo.get(taskDV2VO.getItemId()));
                tableDetail.put("qty", null == taskDV2VO.getCheck2Qty() ? taskDV2VO.getCheckQty() : taskDV2VO.getCheck2Qty());
                table.add(tableDetail);
            }
            data.setHead(head);
            data.setTable(table);
            datas.add(data);
        }
        PrintData printData = new PrintData();
        printData.setPrintName("");
        printData.setData(datas);
        return printData;
    }

    @Override
    @Transactional
    @TxTransaction
    public RestMessage<InnerCheckConfirmCheckVO> confirmCheck(CheckConfirmDTO checkConfirmDTO) {
        InnerCheckConfirmCheckVO result = new InnerCheckConfirmCheckVO();
        /**
         * 无需判断，只复核当前周转箱
         */
//        result.setIsComplete(0);
//        //如果运单号为空，则返回全部复核完成
//        if(StringUtils.isBlank(checkConfirmDTO.getShipBillNo())){
//            result.setIsComplete(1);
//        }else {
//            List<BarcodeHDO> lclNoList = barcodeHMapper.getLclNo(checkConfirmDTO.getStationCode(), null, '0', null);
//            if(lclNoList != null && lclNoList.size() > 0){
//                result.setMsg("同票号（运单号）\\订单中还有未内复核的周转筐，请确认封箱或者继续等待");
//            }else {
//                result.setIsComplete(1);
//            }
//
//        }
//        //
//        if(result.getIsComplete() == 1){
//            containerCheckConfirm(checkConfirmDTO);
//        }
        return containerCheckConfirm(checkConfirmDTO);
    }

    @Flow(name = "内复核确认", category = "出库/复核管理/内复核/内复核确认", index = 7, description = "1、校验：复核数量&是否打印等 2、更新pick_task_h status=10 3、更新barcode_h check_status=0")
    @Transactional
    public RestMessage<InnerCheckConfirmCheckVO> containerCheckConfirm(CheckConfirmDTO checkConfirmDTO) {
        List<InnerCheckDDO> innerCheckDs = validInnerCheckConfirmFunc.validInnerCheckConfirmFunc(checkConfirmDTO);
        return innerCheckConfirmFunc.innerCheckConfirmFunc(checkConfirmDTO, innerCheckDs);
    }

    @FuncImpl(name = "内复核确认校验", func = ValidInnerCheckConfirmFunc.class, description = "内复核确认校验")
    public List<InnerCheckDDO> validInnerCheckConfirmFunc(CheckConfirmDTO checkConfirmDTO) {
        String containerCode = checkConfirmDTO.getContainerCode();
        String billGroupId = checkConfirmDTO.getBillGroupId();
        String stationCode = checkConfirmDTO.getStationCode();
        AssertUtils.isNotBlank(billGroupId);
        AssertUtils.isNotBlank(containerCode);
        AssertUtils.isNotBlank(stationCode);
        //商品组装,查询两个服务

        return innerCheckDMapper.getInnerCheckD(billGroupId, containerCode);
    }

    @FuncImpl(name = "内复核确认", func = InnerCheckConfirmFunc.class, description = "内复核确认")
    public RestMessage<InnerCheckConfirmCheckVO> innerCheckConfirmFunc(CheckConfirmDTO checkConfirmDTO, List<InnerCheckDDO> innerCheckDs) {
        String billGroupId = checkConfirmDTO.getBillGroupId();
        String containerCode = checkConfirmDTO.getContainerCode();
        String stationCode = checkConfirmDTO.getStationCode();
        Criteria criteria = Criteria.forClass(PickTaskH.class);
        criteria.restrictionBuilder()
                .eq("billGroupId", billGroupId)
                .eq("stationCode", stationCode)
                .eq("containerCode", containerCode)
                .ne("status", "10");
        criteria.buildRestriction();
        List<PickTaskH> pickTaskHS = pickTaskHMapper.findByCriteria(criteria);
        AssertUtils.isNotEmpty(pickTaskHS, "未查询到拣货任务！");
        PickTaskH pickTaskH = pickTaskHS.get(0);
        List<String> itemIds = innerCheckDs.stream().map(InnerCheckDDO::getItemId).distinct().collect(Collectors.toList());
        RestMessage<List<WaveItemInfoVO>> waveItemInfos = itemClient.getWaveItemInfo(itemIds);
        List<WaveItemInfoVO> itemVOs = waveItemInfos.getData();
        if (innerCheckDs.size() > 0 && itemVOs != null && itemVOs.size() > 0) {
            for (InnerCheckDDO innerCheckDDO : innerCheckDs) {
                for (WaveItemInfoVO waveItemInfoVO : itemVOs) {
                    if (innerCheckDDO.getItemId().equals(waveItemInfoVO.getItemId())) {
                        innerCheckDDO.setItemName(waveItemInfoVO.getItemName());
                        innerCheckDDO.setNeedDoubleCheck(Integer.parseInt(waveItemInfoVO.getItemHealthIsDouble()));
                        break;
                    }
                }
            }
        }

        List<InnerCheckDDO> collect = innerCheckDs.stream().filter(innerCheckDDO -> !isItemChecked(innerCheckDDO)).collect(Collectors.toList());
        if (!collect.isEmpty()) {
            String itemNames = collect.stream().filter(x -> x.getNeedDoubleCheck() == 1).
                    filter(x -> (StringUtils.isBlank(x.getChecker1()) ||
                            StringUtils.isBlank(x.getChecker2()) || x.getChecker1().equals(x.getChecker2()))).
                    map(InnerCheckDDO::getItemName).collect(Collectors.joining(","));
            if (!StringUtils.isBlank(itemNames)) {
                throw new BizException("商品需要2次复核，请复核员2 登录并完成复核后再确认: " + itemNames);
            }
            String failedItems = collect.stream().map(InnerCheckDDO::getItemName).collect(Collectors.joining(","));
            throw new BizException("还有商品未复核完成: " + failedItems);
        }


        List<LclCheckResultVO> lclCheckResultVOS = innerCheckHMapper.checkLclDetail(billGroupId, containerCode);
        if (!lclCheckResultVOS.isEmpty()) {
            return RestMessage.doError("复核失败，请调整拼箱商品数量后再确认");
        }
        List<BarcodeHDO> lclNos;
        lclNos = barcodeHMapper.getLclNo(stationCode, billGroupId, null, null);
        if (lclNos == null || lclNos.size() == 0) {
            throw new BizException("拼箱数据不能为空");
        }
        List<Integer> lclLists = lclNos.stream().
                map(BarcodeHDO::getLclNo).collect(Collectors.toList());
        Set<Integer> lclSet = new HashSet<>(lclLists);
        if (lclLists.size() != lclSet.size()) {
            throw new BizException("存在重复拼箱，请删除后确认");
        }
        Integer pickTaskType = pickTaskH.getPickTaskType();
        for (BarcodeHDO barcodeH : lclNos) {
            if (barcodeH.getLclNo() != null) {
                String barcodeId = barcodeH.getId();
                List<BarCodeDDO> barCodeDDOS = barcodeDMapper.findByMap(MapUtils.put("barcodeId", barcodeId).getMap(), BarCodeDDO.class);
                if (barCodeDDOS.isEmpty()) {
                    barcodeHMapper.deleteById(barcodeId, BarcodeHDO.class);
                    log.info("拼箱" + barcodeH.getLclNo() + "为空，请检查或者刪除该拼箱确认，barcodeId=" + barcodeId);
                    continue;
                    //throw new BizException("拼箱" + barcodeH.getLclNo() + "为空，请检查或者刪除该拼箱确认");
                }
                for (BarCodeDDO barCodeDDO : barCodeDDOS) {
                    if (pickTaskType != null && pickTaskType == 2) {
                        for (InnerCheckDDO innerCheckDDO : innerCheckDs) {
                            if (innerCheckDDO.getItemId() != null && innerCheckDDO.getItemId().equals(barCodeDDO.getItemId())) {
                                WaveBillDDO billDDO = billDMapper.findById(innerCheckDDO.getBillDId(), WaveBillDDO.class);
                                if (billDDO != null && billDDO.getCaseConversionRate() != null) {
                                    BigDecimal caseConversionRate = billDDO.getCaseConversionRate();
                                    if ((barCodeDDO.getQty().floatValue() / (caseConversionRate.floatValue())) > 1) {
                                        throw new PrologException("商品编号为：【" + barCodeDDO.getItemCode() + "】规定要求必须整件拆分");
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        List<PickTaskD> pickTaskDS = pickTaskDMapper.getPickTaskdByContainerCode(checkConfirmDTO.getContainerCode());
        if (CollUtil.isEmpty(pickTaskDS)) {
            throw new BizException("根据容器号查询拣货明细失败");
        }

        Criteria crt = Criteria.forClass(BarcodeHDO.class);
        Restriction r1 = Restrictions.eq("billGroupId", billGroupId);
        Restriction r2 = Restrictions.eq("containerCode", containerCode);
        crt.setRestriction(Restrictions.and(r1, r2));
        barcodeHMapper.updateMapByCriteria(MapUtils.put("checkStatus", "0").getMap(), crt);

        //校验同票号（运单号）订单中是否还有未内复核的周转筐
        InnerCheckConfirmCheckVO result = new InnerCheckConfirmCheckVO();
        /**
         * 只确认当前周转箱
         */
        result.setIsShow(0);
        result.setIsComplete(0);
        //判断是否还有未复核完成的任务明细
        List<PickTaskD> pickTaskDList = pickTaskDS.stream().filter(item -> Integer.parseInt(item.getFlowStatus()) < 5).collect(Collectors.toList());
        if (pickTaskDList.size() > 0) {
            throw new BizException("当前周转箱中还有未内复核的商品");
        } else {
            //判断是否存在未封箱的拼箱
            //如果当前任务存在未封箱的拼箱，则进行提示，均已封箱则正常返回即可
            List<BarcodeHDO> barcodeHDOList = barcodeHMapper.getUsingByPickTaskId(pickTaskH.getPickTaskId());
            if (CollUtil.isNotEmpty(barcodeHDOList)) {
                //判断是否是当前复核台当前订单下的最后一个周转箱
                Integer unConfirmContainerCnt = barcodeHMapper.getUnConfirmContainerCnt(containerCode, null, billGroupId);
                if (unConfirmContainerCnt == 0) {
                    //判断是否有未打印的拼箱标签
                    List<BarcodeHDO> unPrintList = barcodeHMapper.getUnPrintByPickTaskId(pickTaskH.getPickTaskId());
                    if (CollUtil.isNotEmpty(unPrintList)) {
                        throw new PrologException("当前周转箱还有标签未打印，请打印完成后确认");
                    }
                }
                result.setIsShow(1);
                result.setMsg("同订单中还有未内复核的周转箱，请确认封箱或者继续等待");
            } else {
                result.setIsComplete(1);
                result.setMsg("复核完成");
            }
        }
        if (result.getIsComplete() == 1) {
            //设置为已经完成
            pickTaskH.setStatus("10");
            pickTaskHMapper.update(pickTaskH);
        }

        //释放周转箱(无分布式事务，不会回滚)
        ContainerQueryVO containerQueryVO = new ContainerQueryVO();
        containerQueryVO.setContainerCode(containerCode);
        containerQueryVO.setWarehouseId(pickTaskH.getWarehouseId());
        warehouseResource.release(containerQueryVO);

        return RestMessage.doSuccess(result);
//        return RestMessage.doSuccess(oneCarOneBillTasks.stream().map(t->t.getContainerCode()).collect(Collectors.toList()));
    }

    private boolean isItemChecked(InnerCheckDDO innerCheckDDO) {
        if (innerCheckDDO.getCheckQty() == null) {
            return false;
        }
        int needDoubleCheck = innerCheckDDO.getNeedDoubleCheck();
        BigDecimal redQty = innerCheckDDO.getRedQty() == null ? BigDecimal.ZERO : innerCheckDDO.getRedQty();
        BigDecimal checkQty = innerCheckDDO.getCheckQty().add(redQty);
        BigDecimal checker2QtyTemp = innerCheckDDO.getChecker2Qty() == null ? BigDecimal.ZERO : innerCheckDDO.getChecker2Qty();
        BigDecimal checker2Qty = checker2QtyTemp.add(redQty);
        BigDecimal pickQty = innerCheckDDO.getPickQty();
        if (needDoubleCheck == 1) {
            return pickQty.equals(checker2Qty);
        }
        return pickQty.equals(checkQty);
    }

    public LclVO queryLcl(
            String stationCode,
            String containerCode,
            String pickTaskDId
    ) {
        LclVO lclVO = new LclVO();
        //查询当前滑道下未封箱的拼箱
        List<PickTaskH> pickTaskHList = pickTaskHMapper.getByTaskDId(pickTaskDId, stationCode, containerCode);
        if (CollUtil.isEmpty(pickTaskHList)) {
            throw new BizException("未找到对应的拣货单");
        }
        PickTaskH pickTaskH = pickTaskHList.get(0);
        BarCodeHVO barCodeHVO = new BarCodeHVO();
        List<BarcodeHDO> lclNos = barcodeHMapper.getLclByStation(stationCode, pickTaskH.getBillId());
        if (lclNos.isEmpty()) {
            log.info("当前订单暂无拼箱");
            //无拼箱则新增一个拼箱
            BarcodeHDO barcodeHDO = this.createLclInDB(stationCode, pickTaskH.getBillGroupId(), null, containerCode, null, 1);
            barCodeHVO.setBarcode(barcodeHDO.getBarcode());
            barCodeHVO.setBarcodeDList(new ArrayList<>());
            barCodeHVO.setLclNo(barcodeHDO.getLclNo());
            barCodeHVO.setBillGroupId(pickTaskH.getBillGroupId());
            barCodeHVO.setStationCode(stationCode);
            barCodeHVO.setStatus(barcodeHDO.getStatus());
            barCodeHVO.setType(barcodeHDO.getType());
            lclVO.setLcls(Collections.singletonList(barCodeHVO));
            return lclVO;
        }
        //返回当前拼箱号即可
        Integer currLclNo = lclNos.get(0).getLclNo();
        BarcodeHDO barcodeHDO = lclNos.get(0);
        barCodeHVO.setBarcode(barcodeHDO.getBarcode());
        barCodeHVO.setBarcodeDList(new ArrayList<>());
        barCodeHVO.setLclNo(barcodeHDO.getLclNo());
        barCodeHVO.setBillGroupId(pickTaskH.getBillGroupId());
        barCodeHVO.setStationCode(stationCode);
        barCodeHVO.setStatus(barcodeHDO.getStatus());
        barCodeHVO.setType(barcodeHDO.getType());
        lclVO.setLcls(Collections.singletonList(barCodeHVO));
        lclVO.setCurrLclNo(currLclNo);
        return lclVO;
    }

    /**
     * 对pick_task_d进行拆分，生成新的行，且未复核确认，未绑定barcoded
     *
     * @param pickTaskDId 待拆分的拣货明细行
     * @param qty         需要拆分出来的数量
     * @return 周转箱信息
     */
    @Override
    @Transactional
    @TxTransaction
    public InnerCheckBillV2VO splitPickTaskD(
            String pickTaskDId,
            BigDecimal qty,
            String itemCode,
            int lclNo,
            String workCode
    ) {
        PickTaskD pickTaskD = pickTaskDMapper.findById(pickTaskDId, PickTaskD.class);
        if (null == pickTaskD) {
            throw new BizException("任务不存在");
        }
        if ((!NumberUtil.isNumber(pickTaskD.getFlowStatus())) || Integer.parseInt(pickTaskD.getFlowStatus()) > 4) {
            throw new BizException("该商品已复核，不可拆分");
        }
        if (pickTaskD.getPickQty().doubleValue() <= qty.doubleValue()) {
            throw new BizException("拆分数量不能大于等于原数量！");
        }
        BigDecimal saleRedQty = pickTaskD.getSaleRedQty() == null ? BigDecimal.ZERO : pickTaskD.getSaleRedQty();

        if ((pickTaskD.getPickQty().subtract(saleRedQty)).doubleValue() <= qty.doubleValue()) {
            throw new BizException("拆分数量不能大于等于实际复核数量！");
        }
        PickTaskD pickTaskD1 = new PickTaskD();
        BeanUtils.copyProperties(pickTaskD, pickTaskD1);
        pickTaskD1.setSrcPickTaskDId(pickTaskD.getPickTaskDId());
        pickTaskD1.setQty(qty);
        pickTaskD1.setPickQty(qty);
        pickTaskD1.setPickTaskDId(String.valueOf(SnowFlakeUtils.getId()));
        pickTaskD1.setFlowStatus("4"); // 状态为内复核已索取
        pickTaskD1.setSaleRedQty(BigDecimal.ZERO);
        pickTaskD1.setCheckerId("");
        pickTaskD1.setCheckerName("");
        pickTaskD1.setCheckQty(null);
        pickTaskD1.setCheckerId2("");
        pickTaskD1.setCheckerName2("");
        pickTaskD1.setChecker2Qty(null);
        pickTaskD1.setBarcodeDId("");
        pickTaskD.setQty(pickTaskD.getQty().subtract(qty));
        if (pickTaskD.getPickQty().doubleValue() > 0) {
            pickTaskD.setPickQty(pickTaskD.getPickQty().subtract(qty));
        }

        // 拣货明细行拆单处理,拆单的同时出库预占同时拆分
        WaveStorageDO waveStorageDO = waveStorageMapper.findById(pickTaskD.getWaveStorageId(), WaveStorageDO.class);
        WaveStorageDO newWaveStorageDO = new WaveStorageDO();
        BeanUtils.copyProperties(waveStorageDO, newWaveStorageDO);
        String waveStorageId = String.valueOf(SnowFlakeUtils.getId());
        newWaveStorageDO.setId(waveStorageId);
        newWaveStorageDO.setQty(qty);
        newWaveStorageDO.setContainerCode(waveStorageDO.getContainerCode());
        waveStorageDO.setQty(waveStorageDO.getQty().subtract(qty));
        pickTaskD1.setWaveStorageId(waveStorageId);

        // 插入和更新
        waveStorageMapper.save(newWaveStorageDO);
        waveStorageMapper.updateMapById(pickTaskD.getWaveStorageId(), MapUtils.put("qty", waveStorageDO.getQty()).getMap(), WaveStorageDO.class);
        pickTaskDMapper.save(pickTaskD1);
        pickTaskDMapper.updateMapById(pickTaskD.getPickTaskDId(),
                MapUtils.put("qty", pickTaskD.getQty())
                        .put("checkQty", null)
                        .put("pickQty", pickTaskD.getPickQty())
                        .put("checker2Qty", null)
                        .getMap(),
                PickTaskD.class);
        PickTaskH pickTaskH = pickTaskHMapper.findById(pickTaskD.getPickTaskId(), PickTaskH.class);
        //自动复核
        CheckInfoDTO checkInfoDTO = new CheckInfoDTO();
        checkInfoDTO.setBillGroupId(pickTaskH.getBillGroupId());
        CheckPickTaskDDTO checkPickTaskDDTO = new CheckPickTaskDDTO();
        checkPickTaskDDTO.setPickTaskDId(pickTaskD1.getPickTaskDId());
        checkPickTaskDDTO.setQty(qty);
        checkInfoDTO.setContainerCode(pickTaskH.getContainerCode());
        checkInfoDTO.setItemCode(itemCode);
        checkInfoDTO.setLclNo(lclNo);
        checkInfoDTO.setStationCode(pickTaskH.getStationCode());
        checkInfoDTO.setWarehouseId(pickTaskH.getWarehouseId());
        checkInfoDTO.setWorkerCode(workCode);
        checkInfoDTO.setCheckPickTaskDDTOList(Collections.singletonList(checkPickTaskDDTO));
        checkInfoDTO.setIsDoubleCheck(0);
        return this.checkItem(checkInfoDTO).getData();
    }

    @Override
    public Map innerCheckConfig(String warehouseId) {
        Map<String, Object> map = new HashMap();
        RestMessage<BaseWmsWarehouseMst> itemResultWarehouse = warehouseResource.warehouse(warehouseId);
        if (itemResultWarehouse.isSuccess() && itemResultWarehouse.getData() != null) {
            BaseWmsWarehouseMst wmsWarehouseMst = itemResultWarehouse.getData();
            //扫描监管码节点：0=整件拣货、零货拣货 1=整件外复核、零货拣货 2=整件拣货、零货内复核 3=整件外复核、零货内复核
            if (wmsWarehouseMst != null) {
                map.put("scanSupervisionCodeStage", wmsWarehouseMst.getScanSupervisionCodeStage());
            }
        }
        RestMessage<BusinessControl> rest = strategyResource.getControlByCode("PICK_TASK_ASK_WAY",
                warehouseId, null, userTemplate.getUserDTO().getEnterpriseId(), null);
        if (rest.isSuccess() && rest.getData() != null) {
            map.put("pickWay", rest.getData().getRegValue());
        }
        return map;
    }

    @Override
    public Map<String, Object> applyForPickSlide(ApplyForPickSlideVO vo) {
        Assert.notNull(vo, "参数不能为空");
        Assert.notEmpty(vo.getMailNo(), "包裹号不能为空");

        List<BarcodeHDO> barcodes = barcodeHMapper.findByMap(MapUtils.put("barcode", vo.getMailNo()).getMap(), BarcodeHDO.class);
        cn.hutool.core.lang.Assert.notEmpty(barcodes, "暂无包裹【{}】的信息", vo.getMailNo());

        BarcodeHDO barcode = barcodes.get(0);
        OmsDoBillHDO omsBill = omsDoBillHMapper.findById(barcode.getBillGroupId(), OmsDoBillHDO.class);
        Assert.notNull(omsBill, "获取出库订单失败");

        List<BaseCommonDict> dicts = RestMsgHelper.getData(commonClient.getDictBasicListByCodes("carrier_slide", null));
        Assert.notEmpty(dicts, "请配置【carrier_slide】承运商-滑道映射关系");

        String carrierCode = omsBill.getCarrierCode();
        if (StrUtil.isEmpty(carrierCode)) {
            return MapUtils.put("slideNo", getErrorSlideway(dicts, omsBill.getEnterpriseId()).getSlidewayNum()).getMap();
        }

        BaseCommonDict baseCommonDict = dicts.stream().filter(d -> StrUtil.isNotEmpty(d.getAttr1()) && d.getAttr1().equals(carrierCode)).findFirst().orElse(null);
        if (baseCommonDict == null) {
            return MapUtils.put("slideNo", getErrorSlideway(dicts, omsBill.getEnterpriseId()).getSlidewayNum()).getMap();
        }

        WmsWhSlideway slideWay = getWmsWhSlideway(baseCommonDict.getDictCode(), omsBill.getEnterpriseId());
        if (slideWay == null) {
            return MapUtils.put("slideNo", getErrorSlideway(dicts, omsBill.getEnterpriseId()).getSlidewayNum()).getMap();
        }
        barcodeHMapper.updateMapById(barcode.getId(),MapUtils.put("slideNo",slideWay.getSlidewayNum()).put("slideTime",new Date()).getMap(),BarcodeHDO.class);
        return MapUtils.put("slideNo", slideWay.getSlidewayNum()).getMap();
    }

    private WmsWhSlideway getErrorSlideway(List<BaseCommonDict> dicts, String enterpriseId) {
        BaseCommonDict errorDict = dicts.stream().filter(s -> "ERROR".equals(s.getAttr1())).findFirst().orElse(null);
        if (errorDict == null) {
            throw new BizException("请配置【carrier_slide】异常滑道映射关系");
        }

        WmsWhSlideway wmsWhSlideway = getWmsWhSlideway(errorDict.getDictCode(), enterpriseId);
        return wmsWhSlideway;
    }

    private WmsWhSlideway getWmsWhSlideway(String slidewayNum, String enterpriseId) {
        WmsWhSlidewayDTO dto = new WmsWhSlidewayDTO();
        dto.setSlidewayNum(slidewayNum);
        dto.setEnterpriseId(enterpriseId);
        List<WmsWhSlideway> slideways = RestMsgHelper.getData(warehouseResource.findByParam(dto));
        Assert.notEmpty(slideways, "暂无可用滑道，请配置");

        WmsWhSlideway wmsWhSlideway = slideways.stream().filter(s -> "1".equals(s.getIsEnable())).findFirst().orElse(null);
        Assert.notNull(wmsWhSlideway, "暂无可用滑道，请启用相关滑道");
        return wmsWhSlideway;
    }
}
