package com.prolog.rdc.tms.app.service.business.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.prolog.common.util.ToolKits;
import com.prolog.framework.common.message.RestMessage;
import com.prolog.framework.core.exception.BizException;
import com.prolog.framework.core.exception.PrologException;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Order;
import com.prolog.framework.core.restriction.Restriction;
import com.prolog.framework.core.restriction.Restrictions;
import com.prolog.product.core.framework.entity.UserEntity;
import com.prolog.product.core.framework.util.PlgFxUsrUtils;
import com.prolog.rdc.tms.app.constant.BussinessConstant;
import com.prolog.rdc.tms.app.constant.DataDictionaryConstant;
import com.prolog.rdc.tms.app.constant.SeriaNnumberConstant;
import com.prolog.rdc.tms.app.constant.StatusConstant;
import com.prolog.rdc.tms.app.constant.enums.DriverReceiveStatusEnum;
import com.prolog.rdc.tms.app.constant.enums.IntelligentLockEnum;
import com.prolog.rdc.tms.app.constant.enums.ShipBillTypeEnum;
import com.prolog.rdc.tms.app.constant.enums.SignInStatusEnum;
import com.prolog.rdc.tms.app.dao.mapper.*;
import com.prolog.rdc.tms.app.model.dto.ScanDispatchBillDTO;
import com.prolog.rdc.tms.app.model.dto.ScanDispatchDetailDTO;
import com.prolog.rdc.tms.app.model.dto.ScanDispatchShipbillDTO;
import com.prolog.rdc.tms.app.model.dto.business.ScanDispatchOrderInfoDTO;
import com.prolog.rdc.tms.app.model.dto.business.ScanDispatchShipbillInfoDTO;
import com.prolog.rdc.tms.app.model.dto.business.ScanOrderInfoDTO;
import com.prolog.rdc.tms.app.model.entity.*;
import com.prolog.rdc.tms.app.model.vo.BillScanVO;
import com.prolog.rdc.tms.app.model.vo.ScanShipbillDetailConfirmVO;
import com.prolog.rdc.tms.app.model.vo.business.*;
import com.prolog.rdc.tms.app.proxy.MainDataProxy;
import com.prolog.rdc.tms.app.service.business.PendingDispatchService;
import com.prolog.rdc.tms.app.service.business.ScanDispatchService;
import com.prolog.rdc.tms.app.service.business.ShipbillNodeTrackingService;
import com.prolog.rdc.tms.app.service.business.ShipbillOperateLogService;
import com.prolog.rdc.tms.app.util.ConvertorUtil;
import com.prolog.rdc.tms.model.dto.basicData.ItemCheckDTO;
import com.prolog.rdc.tms.model.entity.SysParamConfigModel;
import com.prolog.rdc.tms.model.entity.SysRefdatadModel;
import com.prolog.rdc.tms.model.vo.basicData.ItemCheckVO;
import com.prolog.rdc.tms.rmi.feign.MaindataFeign;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 扫描派车实现层
 *
 * @author lhb
 * @since 2022-04-26 14:45:00
 */
@Service
@Slf4j
public class ScanDispatchServiceImpl implements ScanDispatchService {

    @Autowired
    private TmsShipbillHeaderMapper tmsShipbillHeaderMapper;

    @Autowired
    private TmsShipbillDetailMapper tmsShipbillDetailMapper;

    @Autowired
    private MaindataFeign maindataTmsResource;

    @Autowired
    private PendingDispatchService pendingDispatchService;

    @Autowired
    private TmsLoadingBillDetailMapper tmsLoadingBillDetailMapper;

    @Autowired
    private ShipbillNodeTrackingService shipbillNodeTrackingService;

    @Autowired
    private ShipbillOperateLogService shipbillOperateLogService;

    @Autowired
    private TmsShipbillTransportLogMapper tmsShipbillTransportLogMapper;

    @Autowired
    private TmsDispatchScanLogMapper tmsDispatchScanLogMapper;

    @Autowired
    private MainDataProxy mainDataProxy;

    @Autowired
    private ConvertorUtil convertorUtil;

    /**
     * 单号扫描
     *
     * @param vo
     * @return
     */
    @Override
    public ScanDispatchBillDTO scanBill(BillScanVO vo) {
        ToolKits.isParamNull(vo, "单号扫描查询请求不为空");

        UserEntity user = PlgFxUsrUtils.getUserEntity();
        ToolKits.isParamNull(user, "当前人员没有操作权限");

        String scanDispatchMethod = mainDataProxy.getSystemParamByCode(DataDictionaryConstant.SCAN_DISPATCH_METHOD);
        ScanDispatchBillDTO scanDispatchBillDTO = null;
        if (DataDictionaryConstant.SCAN_DISPATCH_METHOD_ORDER_ITEM.equals(scanDispatchMethod)) {
            scanDispatchBillDTO = scanOrderNo(vo, user);
            if (ToolKits.isEmpty(scanDispatchBillDTO)) {
                vo.setScanDispatchMethod(scanDispatchMethod);
                scanDispatchBillDTO = scanShipbillDetail(vo);
            }
        } else if (DataDictionaryConstant.SCAN_DISPATCH_METHOD_SHIPBILL_ITEM.equals(scanDispatchMethod)) {
            vo.setScanDispatchMethod(scanDispatchMethod);
            vo.setShipbillNo(vo.getBillCode());
            String billCode = vo.getBillCode();
            vo.setBillCode(null);
            scanDispatchBillDTO = scanShipbill(vo, user, billCode);
            if (ToolKits.isEmpty(scanDispatchBillDTO)) {
                vo.setBillCode(billCode);
                vo.setShipbillNo(null);
                scanDispatchBillDTO = scanShipbillDetail(vo);
            }
        } else {
            throw new BizException("不支持的扫描派车类型");
        }
        scanDispatchBillDTO.setBillCode(vo.getBillCode());
        return scanDispatchBillDTO;
    }

    private ScanDispatchBillDTO scanOrderNo(BillScanVO vo, UserEntity user) {
        TmsDispatchScanLogModel logModel = new TmsDispatchScanLogModel();
        logModel.setOrderNo(vo.getBillCode());
        logModel.setBillType("1");
        checkScanBill(logModel);
        ScanDispatchBillDTO scanDispatchBillDTO = getShipbill(vo, user);
        if (ToolKits.isEmpty(scanDispatchBillDTO)) {
            return null;
        }
        List<TmsDispatchScanLogModel> scanLogModels = Lists.newArrayList();

        scanDispatchBillDTO.getScannedShipbillList().stream().forEach(model -> {
            TmsDispatchScanLogModel logModel1 = new TmsDispatchScanLogModel();
            logModel1.setShipbillId(model.getShipbillId());
            logModel1.setOrderNo(vo.getBillCode());
            logModel1.setBillType("1");
            getTmsDispatchScanLogModel(logModel1);
            scanLogModels.add(logModel1);
        });
        if (ToolKits.isNotEmpty(scanLogModels)) {
            tmsDispatchScanLogMapper.saveBatch(scanLogModels);
        }
        return scanDispatchBillDTO;
    }

    private void checkScanBill(TmsDispatchScanLogModel logModel) {
        List<TmsDispatchScanLogModel> models = getTmsDispatchScanLogModels(logModel);
        if (ToolKits.isNotEmpty(models) && ToolKits.isNotEmpty(logModel.getOrderNo())) {
            throw new BizException(StrUtil.format("订单{}已扫描", logModel.getOrderNo()));
        }
        if (ToolKits.isNotEmpty(models) && ToolKits.isNotEmpty(logModel.getShipbillId())) {
            throw new BizException(StrUtil.format("运单{}已扫描", logModel.getShipbillId()));
        }
    }

    private ScanDispatchBillDTO scanShipbill(BillScanVO vo, UserEntity user, String billCode) {
        TmsDispatchScanLogModel logModel = new TmsDispatchScanLogModel();
        logModel.setBillType("2");
        logModel.setShipbillId(billCode);
        checkScanBill(logModel);

        ScanDispatchBillDTO scanDispatchBillDTO = getShipbill(vo, user);
        if (ToolKits.isEmpty(scanDispatchBillDTO)) {
            return null;
        }
        List<TmsDispatchScanLogModel> scanLogModels = Lists.newArrayList();

        scanDispatchBillDTO.getScannedShipbillList().stream().forEach(model -> {
            TmsDispatchScanLogModel logModel1 = new TmsDispatchScanLogModel();
            logModel1.setShipbillId(model.getShipbillId());
            logModel1.setOrderNo(model.getOrderNo());
            logModel1.setBillType("2");
            getTmsDispatchScanLogModel(logModel1);
            scanLogModels.add(logModel1);
        });
        if (ToolKits.isNotEmpty(scanLogModels)) {
            tmsDispatchScanLogMapper.saveBatch(scanLogModels);
        }
        return scanDispatchBillDTO;
    }

    private ScanDispatchBillDTO scanShipbillDetail(BillScanVO vo) {
        ScanDispatchBillDTO scanDispatchBillDTO = getShipbillDetail(vo);
        if (ToolKits.isEmpty(scanDispatchBillDTO) || ToolKits.isEmpty(scanDispatchBillDTO.getScannedShipbillDetailList())) {
            throw new PrologException(StrUtil.format("扫描商品{}不存在", vo.getBillCode()));
        }

        List<TmsDispatchScanLogModel> scanLogModels = Lists.newArrayList();
        scanDispatchBillDTO.getScannedShipbillDetailList().stream().forEach(detail -> {
            TmsDispatchScanLogModel logModel = new TmsDispatchScanLogModel();
            logModel.setBillType("3");
            logModel.setOrderNo(detail.getOrderNo());
            logModel.setShipbillDetailId(detail.getId());
            logModel.setShipbillId(detail.getShipbillId());
            logModel.setItemCode(detail.getItemCode());
            logModel.setPcs(detail.getPcs());
            logModel.setApplyPcs(BigDecimal.ZERO);
            logModel.setStatus(10);
            List<TmsDispatchScanLogModel> models = getTmsDispatchScanLogModels(logModel);
            if (ToolKits.isEmpty(models)) {
                getTmsDispatchScanLogModel(logModel);
                scanLogModels.add(logModel);
            }
        });
        if (ToolKits.isNotEmpty(scanLogModels)) {
            tmsDispatchScanLogMapper.saveBatch(scanLogModels);
        }
        return scanDispatchBillDTO;
    }

    private ScanDispatchBillDTO getShipbill(BillScanVO vo, UserEntity user) {
        vo.setDeliveryMethodType(DataDictionaryConstant.DELIVERY_METHOD_TYPE_SEND);
        vo.setEnterpriseId(user.getEnterpriseId());
        vo.setUserType(user.getUserType());
        // 权限控制
        if (ObjectUtil.equal(user.getUserType(), BussinessConstant.USER_TYPE_ZY)) {
            if (user.getOwnedAllDataAuthFlag().equals(BussinessConstant.USER_DEPARTMENT_AUTH_Y)) {
                vo.setDepts(user.getDepts().stream().toArray(String[]::new));
                vo.setIsDept(StatusConstant.IS_DEPT_YES);
            } else {
                vo.setDepts(user.getDepts().stream().toArray(String[]::new));
                vo.setIsDept(StatusConstant.IS_DEPT_NO);
                // 货主
                vo.setConsignors(user.getConsignors().stream().toArray(Long[]::new));
                // 客户
                vo.setCustomers(user.getCustomers().stream().toArray(Long[]::new));
                // 供应商
                vo.setSuppliers(user.getSuppliers().stream().toArray(Long[]::new));
                // 仓库
                vo.setWsarehouses(user.getWarehouses().stream().toArray(Long[]::new));
            }
        } else if (ObjectUtil.equal(user.getUserType(), BussinessConstant.USER_TYPE_CYS)) {

            vo.setUnitId(Long.valueOf(user.getLoginUserCompanyInfo().getId()));
        } else {
            throw new PrologException("当前人员没有操作权限！");
        }
        List<ScanDispatchShipbillDTO> tmsShipbillHeaderModels = tmsShipbillHeaderMapper.scanDispatchShipbill(vo);

        if (ToolKits.isEmpty(tmsShipbillHeaderModels)) {
            return null;
        }
        // 数据字典翻译数据集合
        List<SysRefdatadModel> dicData = convertorUtil.getDictionary(new String[]{DataDictionaryConstant.CONSIGNEE_TYPE, // 收货方类型
                DataDictionaryConstant.SHIPPER_TYPE, //发货方类型
                DataDictionaryConstant.TEMPERATURE_REQUIREMENT, //温度要求
                DataDictionaryConstant.DELIVERY_METHOD_TYPE,  // 配送方式
                DataDictionaryConstant.TRANSPORT_TYPE, // 运输类型
                DataDictionaryConstant.ORDER_TYPE, // 订单类型
                DataDictionaryConstant.ORDER_BUSINESS_TYPE, // 订单业务类型
                DataDictionaryConstant.SHIPBILL_STATUS
        });
        ScanDispatchBillDTO billDTO = new ScanDispatchBillDTO();
        List<String> scannedItemCodes = Lists.newArrayList();
        List<String> totalItemCodes = Lists.newArrayList();

        tmsShipbillHeaderModels.stream().forEach(model -> {
            if (ToolKits.isNotEmpty(model.getConsigneeLongitude()) && ToolKits.isNotEmpty(model.getConsigneeLatitude())) {
                model.setConsigneePositioning(StatusConstant.POSITIONING_YDW);
            } else {
                model.setConsigneePositioning(StatusConstant.POSITIONING_WDW);
            }
            // 发货方位置(经纬度判断)
            if (ToolKits.isNotEmpty(model.getShipperLongitude()) && ToolKits.isNotEmpty(model.getShipperLatitude())) {
                model.setShipperPositioning(StatusConstant.POSITIONING_YDW);
            } else {
                model.setShipperPositioning(StatusConstant.POSITIONING_WDW);
            }

            StringBuffer consigneeProvinceCityDistrict = new StringBuffer();
            // 目的地
            consigneeProvinceCityDistrict.append(model.getConsigneeProvinceName());
            consigneeProvinceCityDistrict.append(model.getConsigneeCityName());
            consigneeProvinceCityDistrict.append(model.getConsigneeDistrictName());
            model.setConsigneeProvinceCityDistrict(consigneeProvinceCityDistrict.toString());
            // 始发地
            StringBuffer shipperProvinceCityDistrict = new StringBuffer();
            shipperProvinceCityDistrict.append(model.getShipperProvinceName());
            shipperProvinceCityDistrict.append(model.getShipperCityName());
            shipperProvinceCityDistrict.append(model.getShipperDistrictName());
            model.setShipperProvinceCityDistrict(shipperProvinceCityDistrict.toString());
            // 类型翻译
            model.setOrderTypeName(ConvertorUtil.getName(dicData, DataDictionaryConstant.ORDER_TYPE, String.valueOf(model.getOrderType())));
            model.setOrderBusinessTypeName(ConvertorUtil.getName(dicData, DataDictionaryConstant.ORDER_BUSINESS_TYPE, String.valueOf(model.getOrderBusinessType())));
            model.setDeliveryMethodTypeName(ConvertorUtil.getName(dicData, DataDictionaryConstant.DELIVERY_METHOD_TYPE, String.valueOf(model.getDeliveryMethodType())));
            model.setTransportTypeName(ConvertorUtil.getName(dicData, DataDictionaryConstant.TRANSPORT_TYPE, String.valueOf(model.getTransportType())));
            model.setTemperatureRequirementName(ConvertorUtil.getName(dicData, DataDictionaryConstant.TEMPERATURE_REQUIREMENT, String.valueOf(model.getTemperatureRequirement())));
            model.setShipperTypeName(ConvertorUtil.getName(dicData, DataDictionaryConstant.SHIPPER_TYPE, String.valueOf(model.getShipperType())));
            model.setConsigneeTypeName(ConvertorUtil.getName(dicData, DataDictionaryConstant.CONSIGNEE_TYPE, String.valueOf(model.getConsigneeType())));

            model.setTotalScanItem(0);
            model.setUnScanItem(0);
            model.setScannedItem(0);
            vo.setShipbillNo(model.getShipbillNo());
            ScanDispatchBillDTO detail1DTO = getShipbillDetail(vo);
            if (ToolKits.isNotEmpty(detail1DTO) && ToolKits.isNotEmpty(detail1DTO.getScannedShipbillDetailList())) {
                model.setScannedShipbillDetailList(detail1DTO.getScannedShipbillDetailList());
                model.setScannedItem(detail1DTO.getScannedShipbillDetailList().stream().map(ScanDispatchDetailDTO::getItemCode).distinct().collect(Collectors.toList()).size());
                scannedItemCodes.addAll(detail1DTO.getScannedShipbillDetailList().stream().map(ScanDispatchDetailDTO::getItemCode).distinct().collect(Collectors.toList()));
                model.setShipbillScannedItemCodes(detail1DTO.getScannedShipbillDetailList().stream().map(ScanDispatchDetailDTO::getItemCode).distinct().collect(Collectors.toList()));
            }
            vo.setShipbillNo(null);
            List<TmsShipbillDetailModel> allShipbillDetails = getShipbillDetails(model.getShipbillId(), user);
            if (ToolKits.isNotEmpty(allShipbillDetails)) {
                model.setTotalScanItem(allShipbillDetails.stream().map(TmsShipbillDetailModel::getItemCode).distinct().collect(Collectors.toList()).size());
                List<TmsShipbillDetailModel> allUnScanShipbillDetails = Lists.newArrayList();
                if (NumberUtil.compare(model.getScannedItem().intValue(), 0) > 0) {
                    List<String> ids = detail1DTO.getScannedShipbillDetailList().stream().map(ScanDispatchDetailDTO::getId).distinct().collect(Collectors.toList());
                    allUnScanShipbillDetails = allShipbillDetails.stream().filter(d -> !ids.contains(d.getId())).collect(Collectors.toList());
                } else if (NumberUtil.compare(model.getScannedItem().intValue(), 0) == 0) {
                    allUnScanShipbillDetails.addAll(allShipbillDetails);
                }
                List<ScanDispatchDetailDTO> unScannShipbillDetailList = Lists.newArrayList();
                allUnScanShipbillDetails.stream().forEach(d -> {
                    ScanDispatchDetailDTO detail2DTO = BeanUtil.copyProperties(d, ScanDispatchDetailDTO.class);
                    detail2DTO.setOrderNo(model.getOrderNo());
                    unScannShipbillDetailList.add(detail2DTO);
                });
                if (ToolKits.isNotEmpty(unScannShipbillDetailList)) {
                    model.setUnScannShipbillDetailList(unScannShipbillDetailList);
                }
                totalItemCodes.addAll(allShipbillDetails.stream().map(TmsShipbillDetailModel::getItemCode).distinct().collect(Collectors.toList()));
                model.setShipbillTotalItemCodes(allShipbillDetails.stream().map(TmsShipbillDetailModel::getItemCode).distinct().collect(Collectors.toList()));
            }
            model.setUnScanItem(NumberUtil.sub(BigDecimal.valueOf(model.getTotalScanItem().longValue()), BigDecimal.valueOf(model.getScannedItem().longValue())).intValue());
        });

        billDTO.setScannedShipbillList(tmsShipbillHeaderModels);
        billDTO.setConsigneeName(tmsShipbillHeaderModels.stream().map(ScanDispatchShipbillDTO::getConsigneeName).distinct().collect(Collectors.joining(",")));
        billDTO.setCodeType("1");

        billDTO.setScannedItemCodes(scannedItemCodes.stream().distinct().collect(Collectors.toList()));
        billDTO.setTotalItemCodes(totalItemCodes.stream().distinct().collect(Collectors.toList()));
        return billDTO;
    }

    private List<TmsShipbillDetailModel> getShipbillDetails(String shipbillId, UserEntity user) {
        Criteria criteriaDetail = Criteria.forClass(TmsShipbillDetailModel.class);
        Restriction r1 = Restrictions.eq(TmsShipbillDetailModel::getEnterpriseId, user.getEnterpriseId());
        Restriction r2 = Restrictions.eq(TmsShipbillDetailModel::getShipbillId, shipbillId);

        criteriaDetail.setRestriction(Restrictions.and(r1, r2));
        return tmsShipbillDetailMapper.findByCriteria(criteriaDetail);
    }

    private void getTmsDispatchScanLogModel(TmsDispatchScanLogModel logModel) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        ToolKits.isParamNull(user, "当前人员没有操作权限");
        String id = mainDataProxy.getCodeNumberByCode(SeriaNnumberConstant.TMS_DISPATCH_SCAN_LOG_ID);
        logModel.setId(id);
        logModel.setStatus(10);
        logModel.setEnterpriseId(user.getEnterpriseId());
        logModel.setEnterpriseName(user.getEnterpriseName());
        logModel.setCreateperson(user.getNickname());
        logModel.setCreatetime(DateUtil.date());
    }

    private List<TmsDispatchScanLogModel> getTmsDispatchScanLogModels(TmsDispatchScanLogModel logModel, String... isDispatch) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        ToolKits.isParamNull(user, "当前人员没有操作权限");
        Criteria criteriaLog = Criteria.forClass(TmsDispatchScanLogModel.class);
        Restriction r21 = Restrictions.eq(TmsDispatchScanLogModel::getCreateperson, user.getNickname());
        Restriction r22 = Restrictions.eq(TmsDispatchScanLogModel::getEnterpriseId, user.getEnterpriseId());
        Restriction r23 = Restrictions.eq(TmsDispatchScanLogModel::getStatus, 10);
        Restriction r24 = Restrictions.eq(TmsDispatchScanLogModel::getBillType, logModel.getBillType());
        Restriction r25 = ToolKits.isEmpty(logModel.getOrderNo()) ? null : Restrictions.eq(TmsDispatchScanLogModel::getOrderNo, logModel.getOrderNo());
        Restriction r26 = ToolKits.isEmpty(logModel.getShipbillId()) ? null : Restrictions.eq(TmsDispatchScanLogModel::getShipbillId, logModel.getShipbillId());
        Restriction r27 = ToolKits.isEmpty(logModel.getShipbillDetailId()) ? null : Restrictions.eq(TmsDispatchScanLogModel::getShipbillDetailId, logModel.getShipbillDetailId());
        criteriaLog.setRestriction(Restrictions.and(r21, r22, r23, r24, r25, r26, r27));
        criteriaLog.setOrder(Order.newInstance().desc(new String[]{"createtime"}));
        List<TmsDispatchScanLogModel> scanLogModels = tmsDispatchScanLogMapper.findByCriteria(criteriaLog);
        if (ToolKits.isEmpty(isDispatch) || !"Y".equals(isDispatch[0])) {
            return scanLogModels;
        }
        List<String> shipbillIds = scanLogModels.stream().map(TmsDispatchScanLogModel::getShipbillId).distinct().collect(Collectors.toList());
        List<TmsShipbillHeaderModel> shipbillHeaderModels = tmsShipbillHeaderMapper.findByIds(TmsShipbillHeaderModel.class, shipbillIds.stream().toArray(), null);
        if (ToolKits.isEmpty(shipbillHeaderModels)) {
            throw new PrologException("运单不存在");
        }
        List<String> processedShipbillIds = Lists.newArrayList();
        shipbillHeaderModels.stream().forEach(model -> {
            if (ToolKits.isEmpty(model.getCarrierShipbillStatus())) {
                if (DataDictionaryConstant.SPLIT_ORDER_STATUS_ED.equals(model.getSplitOrderStatus())
                        || NumberUtil.isGreater(BigDecimal.valueOf(model.getStatus()), BigDecimal.valueOf(DataDictionaryConstant.SHIPBILL_STATUS_REVIEW))) {
                    processedShipbillIds.add(model.getShipbillId());
                }
            } else {
                if (DataDictionaryConstant.SPLIT_ORDER_STATUS_ED.equals(model.getCarrierSplitOrderStatus())
                        || NumberUtil.isGreater(BigDecimal.valueOf(model.getCarrierShipbillStatus()), BigDecimal.valueOf(DataDictionaryConstant.CARRIER_SHIPBILL_STATUS_AUDITED))) {
                    processedShipbillIds.add(model.getShipbillId());
                }
            }
        });
        if (ToolKits.isNotEmpty(processedShipbillIds)) {
            scanLogModels = scanLogModels.stream().filter(h -> !processedShipbillIds.contains(h.getShipbillId())).collect(Collectors.toList());
            updateTmsDispatchScanLogModels(user,processedShipbillIds);
        }
        return scanLogModels;
    }

    public void shipBillOut(BillScanVO vo) {
        ToolKits.isParamNull(vo, "剔除请求不为空");
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        ToolKits.isParamNull(user, "当前人员没有操作权限");

        Criteria criteriaLog = Criteria.forClass(TmsDispatchScanLogModel.class);
        Restriction r21 = Restrictions.eq(TmsDispatchScanLogModel::getCreateperson, user.getNickname());
        Restriction r22 = Restrictions.eq(TmsDispatchScanLogModel::getEnterpriseId, user.getEnterpriseId());
        Restriction r23 = Restrictions.eq(TmsDispatchScanLogModel::getStatus, 10);
        Restriction r24 = ToolKits.isEmpty(vo.getOrderNo()) ? null : Restrictions.eq(TmsDispatchScanLogModel::getOrderNo, vo.getOrderNo());
        Restriction r25 = ToolKits.isEmpty(vo.getShipbillId()) ? null : Restrictions.eq(TmsDispatchScanLogModel::getShipbillId, vo.getShipbillId());
        criteriaLog.setRestriction(Restrictions.and(r21, r22, r23, r24, r25));
        tmsDispatchScanLogMapper.deleteByCriteria(criteriaLog);
    }

    private void updateTmsDispatchScanLogModels(UserEntity user, List<String> ids) {
        Criteria criteriaLog = Criteria.forClass(TmsDispatchScanLogModel.class);
        Restriction r21 = Restrictions.eq(TmsDispatchScanLogModel::getCreateperson, user.getNickname());
        Restriction r22 = Restrictions.eq(TmsDispatchScanLogModel::getEnterpriseId, user.getEnterpriseId());
        Restriction r23 = Restrictions.eq(TmsDispatchScanLogModel::getStatus, 10);
        Restriction r24 = ToolKits.isEmpty(ids) ? null : Restrictions.in(TmsDispatchScanLogModel::getShipbillId, ids);
        criteriaLog.setRestriction(Restrictions.and(r21, r22, r23,r24));
        Map<String, Object> map = Maps.newHashMap();
        map.put("status", 20);
        map.put("updatetime", DateUtil.date());
        map.put("updateperson", user.getNickname());
        tmsDispatchScanLogMapper.updateMapByCriteria(map, criteriaLog);
    }

    private ScanDispatchBillDTO getShipbillDetail(BillScanVO vo) {
        TmsDispatchScanLogModel logModel = new TmsDispatchScanLogModel();
        List<TmsDispatchScanLogModel> detailLogModels = Lists.newArrayList();
        if (ToolKits.isNotEmpty(vo.getShipbillNo())) {
            logModel.setBillType("3");
            detailLogModels = getTmsDispatchScanLogModels(logModel);
            if (ToolKits.isEmpty(detailLogModels)) {
                return null;
            }
            vo.setShipbillDetailIds(detailLogModels.stream().map(TmsDispatchScanLogModel::getShipbillDetailId).distinct().collect(Collectors.toList()));
        } else if (ToolKits.isEmpty(vo.getShipbillId()) && ToolKits.isEmpty(vo.getShipbillNo())) {
            if (DataDictionaryConstant.SCAN_DISPATCH_METHOD_ORDER_ITEM.equals(vo.getScanDispatchMethod())) {
                logModel.setBillType("1");
            } else if (DataDictionaryConstant.SCAN_DISPATCH_METHOD_SHIPBILL_ITEM.equals(vo.getScanDispatchMethod())) {
                logModel.setBillType("2");
            }
            List<TmsDispatchScanLogModel> logModels = getTmsDispatchScanLogModels(logModel);
            if (ToolKits.isEmpty(logModels)) {
                throw new PrologException("请先扫描订单或者运单");
            }
            vo.setOrderNos(logModels.stream().map(TmsDispatchScanLogModel::getOrderNo).distinct().collect(Collectors.toList()));
            vo.setShipbillIds(logModels.stream().map(TmsDispatchScanLogModel::getShipbillId).distinct().collect(Collectors.toList()));
        }
        List<ScanDispatchDetailDTO> tmsShipbillDetailModels = tmsShipbillHeaderMapper.scanDispatchShipbillDetail(vo);
        if (ToolKits.isEmpty(tmsShipbillDetailModels)) {
            return null;
        }
        // 数据字典翻译数据集合
        List<SysRefdatadModel> dicData = convertorUtil.getDictionary(new String[]{DataDictionaryConstant.PACK_TYPE, // 包装类型
                DataDictionaryConstant.CARGO_THERMOSPHERE //货物温层
        });
        ScanDispatchBillDTO billDTO = new ScanDispatchBillDTO();
        for (ScanDispatchDetailDTO detail : tmsShipbillDetailModels) {
            // 类型翻译
            detail.setPackTypeName(ConvertorUtil.getName(dicData, DataDictionaryConstant.PACK_TYPE, String.valueOf(detail.getPackType())));
            detail.setCargoThermosphereName(ConvertorUtil.getName(dicData, DataDictionaryConstant.CARGO_THERMOSPHERE, String.valueOf(detail.getCargoThermosphere())));

            detail.setApplyPcs(BigDecimal.ZERO);
            if (ToolKits.isNotEmpty(detailLogModels)) {
                List<TmsDispatchScanLogModel> currDetailLogModels =
                        detailLogModels.stream().filter(d -> d.getShipbillDetailId().equals(detail.getId())).collect(Collectors.toList());
                if (ToolKits.isNotEmpty(currDetailLogModels)) {
                    detail.setApplyPcs(currDetailLogModels.get(0).getApplyPcs());
                }
            }
        }
        billDTO.setScannedShipbillDetailList(tmsShipbillDetailModels);
        billDTO.setCodeType("3");

        return billDTO;
    }

    public ScanDispatchOrderInfoDTO getScanDispatchInfo(BillScanVO vo) {
        ToolKits.isParamNull(vo, "获取扫描订单请求不为空");
        ToolKits.isParamNull(vo.getDataType(), "获取数据类型不为空");
        ToolKits.isParamNull(vo.getScanDispatchMethod(), "扫描方式不为空");
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        ToolKits.isParamNull(user, "当前人员没有操作权限");
        ScanDispatchOrderInfoDTO orderBillDTO = new ScanDispatchOrderInfoDTO();
        orderBillDTO.setScanned(0);
        orderBillDTO.setUnScan(0);
        orderBillDTO.setTotalScan(0);
        orderBillDTO.setAllScannedItem(0);
        orderBillDTO.setAllTotalScanItem(0);
        orderBillDTO.setAllUnScanItem(0);

        if (DataDictionaryConstant.SCAN_DISPATCH_METHOD_SHIPBILL_ITEM.equals(vo.getScanDispatchMethod())) {
            ScanDispatchShipbillInfoDTO shipbillInfoDTO = getScanShipbillInfo(vo);
            orderBillDTO.setShipbillInfoDTO(shipbillInfoDTO);
        } else if (DataDictionaryConstant.SCAN_DISPATCH_METHOD_ORDER_ITEM.equals(vo.getScanDispatchMethod())) {
            getScanOrderInfo(vo, orderBillDTO);
        }
        return orderBillDTO;
    }

    public void getScanOrderInfo(BillScanVO vo, ScanDispatchOrderInfoDTO orderBillDTO) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();

        TmsDispatchScanLogModel logModel = new TmsDispatchScanLogModel();
        if (ToolKits.isNotEmpty(vo.getItemCode())) {
            logModel.setBillType("3");
            logModel.setItemCode(vo.getItemCode());
        } else {
            logModel.setBillType("1");
            logModel.setOrderNo(vo.getOrderNo());
        }

        List<TmsDispatchScanLogModel> logModels = getTmsDispatchScanLogModels(logModel);
        if (ToolKits.isEmpty(logModels)) {
            return;
        }
        vo.setShipbillIds(logModels.stream().map(TmsDispatchScanLogModel::getShipbillId).collect(Collectors.toList()));
        ScanDispatchBillDTO billDTO = getShipbill(vo, user);
        if (ToolKits.isEmpty(billDTO)) {
            return;
        }
        Map<Object, List<ScanDispatchShipbillDTO>> scannedOrderMaps = Maps.newHashMap();

        List<ScanDispatchShipbillDTO> dispatchShipBillList = billDTO.getScannedShipbillList();
        if (Integer.valueOf(3).equals(vo.getDataType())) {
            dispatchShipBillList = dispatchShipBillList.stream().filter(h -> ToolKits.isNotEmpty(h.getScannedShipbillDetailList())).collect(Collectors.toList());
        }
        if (Integer.valueOf(4).equals(vo.getDataType())) {
            dispatchShipBillList = dispatchShipBillList.stream().filter(h -> ToolKits.isNotEmpty(h.getUnScannShipbillDetailList())).collect(Collectors.toList());
        }
        if (ToolKits.isEmpty(dispatchShipBillList)) {
            return;
        }
        scannedOrderMaps = dispatchShipBillList.stream().collect(Collectors.groupingBy(o -> o.getOrderNo()));
        if (ToolKits.isEmpty(scannedOrderMaps)) {
            return;
        }
        List<ScanOrderInfoDTO> scannedOrderList = Lists.newArrayList();
        for (Object obj : scannedOrderMaps.keySet()) {
            List<ScanDispatchShipbillDTO> shipbillList = scannedOrderMaps.get(obj);
            ScanOrderInfoDTO orderInfoDTO = new ScanOrderInfoDTO();
            orderInfoDTO.setScannedShipbillList(shipbillList);

            orderInfoDTO.setOrderNo(obj.toString());
            orderInfoDTO.setScannedItem(0);
            orderInfoDTO.setUnScanItem(0);
            orderInfoDTO.setTotalScanItem(0);
            orderInfoDTO.setShipBillNums(shipbillList.size());

            BigDecimal totPcs = BigDecimal.ZERO;
            List<String> shipbillScannedItemCodes = Lists.newArrayList();
            List<String> shipbillTotalItemCodes = Lists.newArrayList();
            for (ScanDispatchShipbillDTO shipbill : shipbillList) {
                if (ToolKits.isNotEmpty(shipbill.getScannedShipbillDetailList())
                        && !Integer.valueOf(4).equals(vo.getDataType())) {
                    totPcs = NumberUtil.add(totPcs, shipbill.getScannedShipbillDetailList().stream().map(d -> BigDecimal.valueOf(d.getPcs().longValue())).reduce(BigDecimal.ZERO,
                            BigDecimal::add));
                }
                if (ToolKits.isNotEmpty(shipbill.getUnScannShipbillDetailList())
                        && !Integer.valueOf(3).equals(vo.getDataType())) {
                    totPcs = NumberUtil.add(totPcs, shipbill.getUnScannShipbillDetailList().stream().map(d -> BigDecimal.valueOf(d.getPcs().longValue())).reduce(BigDecimal.ZERO,
                            BigDecimal::add));
                }
                if (ToolKits.isNotEmpty(shipbill.getShipbillScannedItemCodes())) {
                    shipbillScannedItemCodes.addAll(shipbill.getShipbillScannedItemCodes());
                }
                if (ToolKits.isNotEmpty(shipbill.getShipbillTotalItemCodes())) {
                    shipbillTotalItemCodes.addAll(shipbill.getShipbillTotalItemCodes());
                }
            }
            orderInfoDTO.setTotalPcs(totPcs);
            orderInfoDTO.setScannedItem(shipbillScannedItemCodes.stream().distinct().collect(Collectors.toList()).size());
            orderInfoDTO.setTotalScanItem(shipbillTotalItemCodes.stream().distinct().collect(Collectors.toList()).size());

            BigDecimal unScanItem = NumberUtil.sub(BigDecimal.valueOf(orderInfoDTO.getTotalScanItem().longValue()), BigDecimal.valueOf(orderInfoDTO.getScannedItem().longValue()));
            orderInfoDTO.setUnScanItem(unScanItem.intValue());

            ScanDispatchShipbillDTO firstDto = shipbillList.get(0);
            orderInfoDTO.setDepartmentName(firstDto.getDepartmentName());
            orderInfoDTO.setConsigneeName(firstDto.getConsigneeName());
            orderInfoDTO.setConsigneeAddress(firstDto.getConsigneeAddress());
            orderInfoDTO.setConsigneeLatitude(firstDto.getConsigneeLatitude());
            orderInfoDTO.setConsigneeLongitude(firstDto.getConsigneeLongitude());
            StringBuffer tel = new StringBuffer();
            if (ToolKits.isNotEmpty(firstDto.getConsigneePhoneNumber())) {
                tel.append(firstDto.getConsigneePhoneNumber());
            } else if (ToolKits.isNotEmpty(firstDto.getConsigneeLandlineNumber())) {
                tel.append(firstDto.getConsigneeLandlineNumber());
            }
            if (ToolKits.isNotEmpty(tel.toString())) {
                orderInfoDTO.setConsigneeTel(tel.toString());
            }
            scannedOrderList.add(orderInfoDTO);
        }

        List<ScanDispatchShipbillDTO> shipbillList =
                billDTO.getScannedShipbillList().stream().filter(shipBill -> shipBill.getOrderNo().equals(logModels.get(0).getOrderNo())).collect(Collectors.toList());
        if (ToolKits.isNotEmpty(shipbillList)) {
            orderBillDTO.setOrderNo(shipbillList.get(0).getOrderNo());
            orderBillDTO.setConsigneeName(shipbillList.get(0).getConsigneeName());
            orderBillDTO.setDepartmentName(shipbillList.get(0).getDepartmentName());
        }
        orderBillDTO.setScannedOrderList(scannedOrderList);
        orderBillDTO.setScanned(scannedOrderMaps.size());

        orderBillDTO.setAllTotalScanItem(billDTO.getTotalItemCodes().stream().distinct().collect(Collectors.toList()).size());
        orderBillDTO.setAllScannedItem(billDTO.getScannedItemCodes().stream().distinct().collect(Collectors.toList()).size());
        BigDecimal allUnScanItem = NumberUtil.sub(BigDecimal.valueOf(orderBillDTO.getAllTotalScanItem().longValue()),
                BigDecimal.valueOf(orderBillDTO.getAllScannedItem().longValue()));
        orderBillDTO.setAllUnScanItem(allUnScanItem.intValue());
    }

    public ScanDispatchShipbillInfoDTO getScanShipbillInfo(BillScanVO vo) {
        ToolKits.isParamNull(vo, "获取扫描运单请求不为空");
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        ToolKits.isParamNull(user, "当前人员没有操作权限");
        ScanDispatchShipbillInfoDTO shipBillDTO = new ScanDispatchShipbillInfoDTO();
        shipBillDTO.setScanned(0);

        TmsDispatchScanLogModel logModel = new TmsDispatchScanLogModel();
        if (ToolKits.isNotEmpty(vo.getItemCode())) {
            logModel.setBillType("3");
            logModel.setItemCode(vo.getItemCode());
        } else {
            logModel.setBillType("2");
            logModel.setShipbillId(vo.getShipbillNo());
        }

        List<TmsDispatchScanLogModel> logModels = getTmsDispatchScanLogModels(logModel);
        if (ToolKits.isEmpty(logModels)) {
            return shipBillDTO;
        }
        vo.setShipbillIds(logModels.stream().map(TmsDispatchScanLogModel::getShipbillId).collect(Collectors.toList()));
        ScanDispatchBillDTO billDTO = getShipbill(vo, user);
        if (ToolKits.isEmpty(billDTO) || ToolKits.isEmpty(billDTO.getScannedShipbillList())) {
            return shipBillDTO;
        }
        List<ScanDispatchShipbillDTO> dispatchShipBillList = billDTO.getScannedShipbillList();
        if (Integer.valueOf(3).equals(vo.getDataType())) {
            dispatchShipBillList = dispatchShipBillList.stream().filter(h -> ToolKits.isNotEmpty(h.getScannedShipbillDetailList())).collect(Collectors.toList());
        }
        if (Integer.valueOf(4).equals(vo.getDataType())) {
            dispatchShipBillList = dispatchShipBillList.stream().filter(h -> ToolKits.isNotEmpty(h.getUnScannShipbillDetailList())).collect(Collectors.toList());
        }
        if (ToolKits.isEmpty(dispatchShipBillList)) {
            return shipBillDTO;
        }

        shipBillDTO.setScannedShipbillList(dispatchShipBillList);
        shipBillDTO.setScanned(billDTO.getScannedShipbillList().size());

        shipBillDTO.setAllTotalScanItem(billDTO.getTotalItemCodes().stream().distinct().collect(Collectors.toList()).size());
        shipBillDTO.setAllScannedItem(billDTO.getScannedItemCodes().stream().distinct().collect(Collectors.toList()).size());
        BigDecimal allUnScanItem = NumberUtil.sub(BigDecimal.valueOf(shipBillDTO.getAllTotalScanItem().longValue()),
                BigDecimal.valueOf(shipBillDTO.getAllScannedItem().longValue()));
        shipBillDTO.setAllUnScanItem(allUnScanItem.intValue());

        List<ScanDispatchShipbillDTO> shipbillList =
                billDTO.getScannedShipbillList().stream().filter(shipBill -> shipBill.getShipbillId().equals(logModels.get(0).getShipbillId())).collect(Collectors.toList());
        if (ToolKits.isNotEmpty(shipbillList)) {
            ScanDispatchShipbillDTO firstDto = shipbillList.get(0);
            shipBillDTO.setShipbillNo(firstDto.getShipbillNo());
            shipBillDTO.setConsigneeName(firstDto.getConsigneeName());
            shipBillDTO.setDepartmentName(firstDto.getDepartmentName());

            shipBillDTO.setConsigneeLatitude(firstDto.getConsigneeLatitude());
            shipBillDTO.setConsigneeLongitude(firstDto.getConsigneeLongitude());
            StringBuffer tel = new StringBuffer();
            if (ToolKits.isNotEmpty(firstDto.getConsigneePhoneNumber())) {
                tel.append(firstDto.getConsigneePhoneNumber());
            } else if (ToolKits.isNotEmpty(firstDto.getConsigneeLandlineNumber())) {
                tel.append(firstDto.getConsigneeLandlineNumber());
            }
            if (ToolKits.isNotEmpty(tel.toString())) {
                shipBillDTO.setConsigneeTel(tel.toString());
            }
        }

        return shipBillDTO;
    }

    public ScanDispatchShipbillInfoDTO getScanShipbillDetailInfo(BillScanVO vo) {
        ToolKits.isParamNull(vo, "获取扫描运单明细请求不为空");
        if (ToolKits.isEmpty(vo.getScanDispatchMethod())) {
            throw new PrologException("扫描派车方式不为空");
        }
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        ToolKits.isParamNull(user, "当前人员没有操作权限");
        ScanDispatchShipbillInfoDTO shipbillInfoDTO = new ScanDispatchShipbillInfoDTO();
        shipbillInfoDTO.setScanned(0);
        shipbillInfoDTO.setUnScan(0);
        shipbillInfoDTO.setTotalScan(0);

        ScanDispatchBillDTO detailDTO = getShipbillDetail(vo);
        if (ToolKits.isEmpty(detailDTO) || ToolKits.isEmpty(detailDTO.getScannedShipbillDetailList())) {
            return shipbillInfoDTO;
        }
        shipbillInfoDTO.setTotalScan(detailDTO.getScannedShipbillDetailList().size());

        vo.setShipbillIds(detailDTO.getScannedShipbillDetailList().stream().map(ScanDispatchDetailDTO::getShipbillId).collect(Collectors.toList()));
        ScanDispatchBillDTO shipbillDTO = getShipbill(vo, user);
        if (ToolKits.isEmpty(shipbillDTO) || ToolKits.isEmpty(shipbillDTO.getScannedShipbillList())) {
            return shipbillInfoDTO;
        }
        shipbillInfoDTO.setScannedShipbillList(shipbillDTO.getScannedShipbillList());
        BigDecimal scanned = shipbillDTO.getScannedShipbillList().stream().map(s -> BigDecimal.valueOf(s.getScannedItem().longValue())).reduce(BigDecimal.ZERO, BigDecimal::add);

        if (ToolKits.isNotEmpty(scanned)) {
            shipbillInfoDTO.setScanned(scanned.intValue());
        }
        shipbillInfoDTO.setUnScan(NumberUtil.sub(BigDecimal.valueOf(Long.valueOf(shipbillInfoDTO.getTotalScan().toString())),
                BigDecimal.valueOf(Long.valueOf(shipbillInfoDTO.getScanned().toString()))).intValue());
        return shipbillInfoDTO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void scanShipbillConfirm(List<ScanShipbillDetailConfirmVO> vos) {
        ToolKits.isParamNull(vos, "确认请求不为空");
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        ToolKits.isParamNull(user, "当前人员没有操作权限");
        TmsDispatchScanLogModel logModel = new TmsDispatchScanLogModel();
        logModel.setBillType("3");
        for (ScanShipbillDetailConfirmVO vo : vos) {
            logModel.setShipbillDetailId(vo.getId());
            List<TmsDispatchScanLogModel> logModels = getTmsDispatchScanLogModels(logModel);
            if (ToolKits.isEmpty(logModels)) {
                throw new PrologException(StrUtil.format("商品{}未扫描", vo.getItemCode()));
            }
            TmsDispatchScanLogModel currDetaiLogModel = logModels.get(0);
            currDetaiLogModel.setApplyPcs(vo.getApplyPcs());
            currDetaiLogModel.setUpdateperson(user.getNickname());
            currDetaiLogModel.setUpdatetime(DateUtil.date());
            tmsDispatchScanLogMapper.update(currDetaiLogModel);
        }
    }

    /**
     * 扫描派车调度
     *
     * @param vo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dispatch(PendingDispatchVO vo) {
        ToolKits.isParamNull(vo, "扫描派车调度请求不为空");

        if (ToolKits.isEmpty(vo.getDriverId())) {
            throw new PrologException("司机,车俩信息不为空");
        }

        TmsDispatchScanLogModel logModel = new TmsDispatchScanLogModel();
        logModel.setBillType("3");
        List<TmsDispatchScanLogModel> logModels = getTmsDispatchScanLogModels(logModel,"Y");
        if (ToolKits.isEmpty(logModels)) {
            throw new PrologException(StrUtil.format("没有已扫描的商品,不能调度"));
        }

        UserEntity user = PlgFxUsrUtils.getUserEntity();
        ToolKits.isParamNull(user, "当前人员没有操作权限");

        // 当前时间
        Date date = DateUtil.date();
        // 装车单明细
        List<String> shipbillIds = logModels.stream().map(TmsDispatchScanLogModel::getShipbillId).distinct().collect(Collectors.toList());

        List<String> dispatchShipbills = Lists.newArrayList();
        List<String> splitShipbills = Lists.newArrayList();
        for (String shipbillId : shipbillIds) {
            TmsShipbillHeaderModel shipbillHeaderModel = tmsShipbillHeaderMapper.findById(shipbillId, TmsShipbillHeaderModel.class);
            if (ToolKits.isEmpty(shipbillHeaderModel)) {
                throw new PrologException(StrUtil.format("运单{}不存在", shipbillId));
            }
            // 获取商品信息
            Criteria criteriaShipbillDetail = Criteria.forClass(TmsShipbillDetailModel.class);
            Restriction r1 = Restrictions.eq("shipbillId", shipbillId);
            Restriction r2 = Restrictions.eq("enterpriseId", user.getEnterpriseId());
            criteriaShipbillDetail.setRestriction(Restrictions.and(r1, r2));
            List<TmsShipbillDetailModel> shipbillDetailModels = tmsShipbillDetailMapper.findByCriteria(criteriaShipbillDetail);
            if (ToolKits.isEmpty(shipbillDetailModels)) {
                continue;
            }
            List<PendingSplitDetailVO> disShipbillDetails = Lists.newArrayList();
            List<PendingSplitDetailVO> noDisShipbillDetails = Lists.newArrayList();
            List<PendingSplitDetailVO> scannedShipbillDetailVOS = Lists.newArrayList();
            logModels.stream().forEach(model -> {
                if (model.getShipbillId().equals(shipbillId)) {
                    PendingSplitDetailVO detailVO = BeanUtil.copyProperties(model, PendingSplitDetailVO.class);
                    detailVO.setId(model.getShipbillDetailId());
                    scannedShipbillDetailVOS.add(detailVO);
                }
            });

            List<TmsShipbillDetailModel> noDisShipbillDetailModels = shipbillDetailModels.stream()
                                                                                         .filter(d -> !scannedShipbillDetailVOS.stream().map(PendingSplitDetailVO::getId)
                                                                                                                               .collect(Collectors.toList())
                                                                                                                               .contains(d.getId()))
                                                                                         .collect(Collectors.toList());
            noDisShipbillDetailModels.stream().forEach(d -> {
                PendingSplitDetailVO noDisDetail = BeanUtil.copyProperties(d, PendingSplitDetailVO.class);
                noDisShipbillDetails.add(noDisDetail);
            });
            for (PendingSplitDetailVO detailVO : scannedShipbillDetailVOS) {
                if (NumberUtil.equals(BigDecimal.ZERO, BigDecimal.valueOf(Long.valueOf(detailVO.getApplyPcs()).longValue()))) {
                    noDisShipbillDetails.add(detailVO);
                    continue;
                }

                if (NumberUtil.equals(detailVO.getPcs(), BigDecimal.valueOf(Long.valueOf(detailVO.getApplyPcs()).longValue()))) {
                    disShipbillDetails.add(detailVO);
                    continue;
                }
                if (NumberUtil.isGreater(detailVO.getPcs(), BigDecimal.valueOf(Long.valueOf(detailVO.getApplyPcs()).longValue()))) {
                    PendingSplitDetailVO tmpDetailVO = BeanUtil.copyProperties(detailVO, PendingSplitDetailVO.class);
                    tmpDetailVO.setPcs(BigDecimal.valueOf(Long.valueOf(detailVO.getApplyPcs()).longValue()));
                    disShipbillDetails.add(tmpDetailVO);

                    detailVO.setPcs(NumberUtil.sub(detailVO.getPcs(), tmpDetailVO.getPcs()));
                    noDisShipbillDetails.add(detailVO);
                    continue;
                }
                if (NumberUtil.isLess(detailVO.getPcs(), BigDecimal.valueOf(Long.valueOf(detailVO.getApplyPcs()).longValue()))) {
                    throw new PrologException(StrUtil.format("运单{}商品{}的件数大于实际件数", shipbillId, detailVO.getItemCode()));
                }
            }
            if (ToolKits.isEmpty(disShipbillDetails)) {
                throw new PrologException(StrUtil.format("运单没有扫描商品"));
            }
            // 按商品拆分运单
            if (ToolKits.isNotEmpty(disShipbillDetails) && ToolKits.isNotEmpty(noDisShipbillDetails)) {
                DispatchSplitVO splitVO = new DispatchSplitVO();
                splitVO.setShipbillId(shipbillId);
                Map<String, List<PendingSplitDetailVO>> goodsMap = Maps.newHashMap();
                goodsMap.put("disReg", disShipbillDetails);
                goodsMap.put("disRegNo", noDisShipbillDetails);

                splitVO.setSplitGoodsMap(goodsMap);
                // 拆分处理
                splitShipbills.addAll(goodsSplit(splitVO, date, user));
            }

            if (ToolKits.isEmpty(splitShipbills)) {
                dispatchShipbills.add(shipbillId);
            } else {
                dispatchShipbills.addAll(splitShipbills);
            }
        }

        vo.setShipbillIds(dispatchShipbills);
        pendingDispatchService.autoDispatch(vo, user, date);

        updateTmsDispatchScanLogModels(user,null);
    }

    // 商品拆分
    public List<String> goodsSplit(DispatchSplitVO splitVO, Date date, UserEntity user) {
        List<String> dispatchShipbills = Lists.newArrayList();
        // 原运单更新
        TmsShipbillHeaderModel tmsShipbillHeaderModel = tmsShipbillHeaderMapper.findById(splitVO.getShipbillId(), TmsShipbillHeaderModel.class);
        Map<String, Object> mapShipbillHeadUpdate = Maps.newHashMap();
        Restriction r23 = null;
        if (user.getUserType().equals(BussinessConstant.USER_TYPE_ZY)) {
            if (tmsShipbillHeaderModel.getSplitOrderStatus().equals(DataDictionaryConstant.SPLIT_ORDER_STATUS_ED)) {
                throw new PrologException("运单【" + tmsShipbillHeaderModel.getShipbillId() + "】已拆分");
            }
            r23 = Restrictions.eq(TmsShipbillHeaderModel::getSplitOrderStatus, DataDictionaryConstant.SPLIT_ORDER_STATUS_UN);

            tmsShipbillHeaderModel.setSplitOrderStatus(DataDictionaryConstant.SPLIT_ORDER_STATUS_ED);
            mapShipbillHeadUpdate.put("splitOrderStatus", DataDictionaryConstant.SPLIT_ORDER_STATUS_ED);
            // 原始主单被拆分时，拆单类型更新为2拆分主单，子单被拆分时，不更新拆单类型
            if (ToolKits.isEmpty(tmsShipbillHeaderModel.getSplitOrderType()) || tmsShipbillHeaderModel.getSplitOrderType().equals(DataDictionaryConstant.SPLIT_ORDER_TYPE_UN)) {
                tmsShipbillHeaderModel.setSplitOrderType(DataDictionaryConstant.SPLIT_ORDER_TYPE_SPLIT_MASTER);
                mapShipbillHeadUpdate.put("splitOrderType", DataDictionaryConstant.SPLIT_ORDER_TYPE_SPLIT_MASTER);
            }
        } else if (user.getUserType().equals(BussinessConstant.USER_TYPE_CYS)) {
            if (tmsShipbillHeaderModel.getCarrierSplitOrderStatus().equals(DataDictionaryConstant.CARRIER_SPLIT_ORDER_STATUS_ED)) {
                throw new PrologException("运单【" + tmsShipbillHeaderModel.getShipbillId() + "】已拆分");
            }
            r23 = Restrictions.eq(TmsShipbillHeaderModel::getCarrierSplitOrderStatus, DataDictionaryConstant.CARRIER_SPLIT_ORDER_STATUS_UN);

            mapShipbillHeadUpdate.put("carrierSplitOrderStatus", DataDictionaryConstant.CARRIER_SPLIT_ORDER_STATUS_ED);
            tmsShipbillHeaderModel.setCarrierSplitOrderStatus(DataDictionaryConstant.CARRIER_SPLIT_ORDER_STATUS_ED);
            // 原始主单被拆分时，拆单类型更新为2拆分主单，子单被拆分时，不更新拆单类型
            if (ToolKits.isEmpty(tmsShipbillHeaderModel.getCarrierSplitOrderType()) || tmsShipbillHeaderModel.getCarrierSplitOrderType().equals(DataDictionaryConstant.CARRIER_SPLIT_ORDER_TYPE_UN)) {
                tmsShipbillHeaderModel.setCarrierSplitOrderType(DataDictionaryConstant.CARRIER_SPLIT_ORDER_TYPE_SPLIT_MASTER);
                mapShipbillHeadUpdate.put("carrierSplitOrderType", DataDictionaryConstant.CARRIER_SPLIT_ORDER_TYPE_SPLIT_MASTER);
            }
            // 更新装车单明细
            Map<String, Object> mapLoadingDetailUpdate = Maps.newHashMap();
            mapLoadingDetailUpdate.put("updateperson", user.getNickname());
            mapLoadingDetailUpdate.put("updatetime", date);
            mapLoadingDetailUpdate.put("carrierSplitOrderStatus", DataDictionaryConstant.CARRIER_SPLIT_ORDER_STATUS_ED);
            if (ToolKits.isEmpty(tmsShipbillHeaderModel.getCarrierSplitOrderType()) || tmsShipbillHeaderModel.getCarrierSplitOrderType().equals(DataDictionaryConstant.CARRIER_SPLIT_ORDER_TYPE_UN)) {
                mapLoadingDetailUpdate.put("carrierSplitOrderType", DataDictionaryConstant.CARRIER_SPLIT_ORDER_TYPE_SPLIT_MASTER);
            }
            Criteria criteriaLoadingDetail = Criteria.forClass(TmsLoadingBillDetailModel.class);
            Restriction r21 = Restrictions.ge(TmsLoadingBillDetailModel::getCarrierShipbillStatus, DataDictionaryConstant.CARRIER_SHIPBILL_STATUS_WAIT_RECEIVE);
            Restriction r22 = Restrictions.eq(TmsLoadingBillDetailModel::getShipbillId, tmsShipbillHeaderModel.getShipbillId());
            criteriaLoadingDetail.setRestriction(Restrictions.and(r21, r22));
            tmsLoadingBillDetailMapper.updateMapByCriteria(mapLoadingDetailUpdate, criteriaLoadingDetail);
        }
        // 更新运单
        Criteria criteriaHead = Criteria.forClass(TmsShipbillHeaderModel.class);
        Restriction r24 = Restrictions.eq(TmsShipbillHeaderModel::getShipbillId, tmsShipbillHeaderModel.getShipbillId());
        criteriaHead.setRestriction(Restrictions.and(r23, r24));
        mapShipbillHeadUpdate.put("updateperson", user.getNickname());
        mapShipbillHeadUpdate.put("updatetime", date);
        long upCnt = tmsShipbillHeaderMapper.updateMapByCriteria(mapShipbillHeadUpdate, criteriaHead);
        if (upCnt == 0) {
            log.info("运单【" + tmsShipbillHeaderModel.getShipbillId() + "】已拆分,或者已调度");
            throw new PrologException("运单【" + tmsShipbillHeaderModel.getShipbillId() + "】已拆分,或者已调度");
        }
        // 原运单日志保存
        ShipbillOperateLogSaveNewVO shipbillOperateLogSaveVO = new ShipbillOperateLogSaveNewVO();
        shipbillOperateLogSaveVO.setShipbillId(tmsShipbillHeaderModel.getShipbillId());
        shipbillOperateLogSaveVO.setShipbillNo(tmsShipbillHeaderModel.getShipbillNo());
        shipbillOperateLogSaveVO.setOperateEvent(DataDictionaryConstant.SHIPBILL_EVENT_NAME_SPLIT);
        shipbillOperateLogSaveVO.setOldStatus(DataDictionaryConstant.SHIPBILL_STATUS_REVIEW);
        shipbillOperateLogSaveVO.setNewStatus(DataDictionaryConstant.SHIPBILL_STATUS_REVIEW);
        shipbillOperateLogSaveVO.setOperateDate(date);
        shipbillOperateLogService.addShipbillOperateLogNewData(shipbillOperateLogSaveVO);

        // 拆分成多个子单
        RestMessage<SysParamConfigModel> restMessageShipbillId = maindataTmsResource.getValue(DataDictionaryConstant.SHIPBILL_ID_CREATE_RULE);
        if (!restMessageShipbillId.isSuccess()) {
            throw new RuntimeException("获取运单ID生成规则的系统参数失败!");
        }
        SysParamConfigModel sysParamConfigModel = restMessageShipbillId.getData();
        int index = 1;
        Map<String, List<PendingSplitDetailVO>> maps = splitVO.getSplitGoodsMap();
        for (String splitType : maps.keySet()) {
            // 运单汇总ID
            String subShipbillId = "";
            // 运单ID生成规则
            if (DataDictionaryConstant.SHIPBILL_ID_CREATE_RULE_AUTO.equals(sysParamConfigModel.getValue())) {
                // 获取运单汇总ID
                RestMessage<String> restMessageAutoCreateShipbillId = maindataTmsResource.getCodeNumberByCode(SeriaNnumberConstant.TMS_SHIPBILL_HEADER_ID, "L");
                if (!restMessageAutoCreateShipbillId.isSuccess()) {
                    throw new RuntimeException("生成运单汇总ID失败");
                }
                subShipbillId = restMessageAutoCreateShipbillId.getData();
            } else {
                StringBuffer buffNo1 = new StringBuffer(splitVO.getShipbillId());
                buffNo1.append(DataDictionaryConstant.SUB_SHIPBILL_NO);
                buffNo1.append(DataDictionaryConstant.SPLIT_SHIPBILL_NO);
                buffNo1.append(index);

                subShipbillId = buffNo1.toString();
            }
            // 商品总件数
            BigDecimal goodsTotalPcs = new BigDecimal(0);
            // 商品总体积
            BigDecimal goodsTotalVolume = new BigDecimal(0);
            // 商品总重量
            BigDecimal goodsTotalWeight = new BigDecimal(0);

            BigDecimal zero = new BigDecimal(0);

            // 保存每个子单中的明细信息
            for (PendingSplitDetailVO splitGoodsDetailVO : maps.get(splitType)) {
                TmsShipbillDetailModel subTmsShipbillDetailModel = new TmsShipbillDetailModel();
                // 运单明细ID
                RestMessage<String> restMessage2 = maindataTmsResource.getCodeNumberByCode(SeriaNnumberConstant.TMS_SHIPBILL_DETAIL_ID, "L");
                if (!restMessage2.isSuccess()) {
                    throw new RuntimeException("生成运单明细ID失败");
                }
                String subShipbillDetailId = restMessage2.getData();

                TmsShipbillDetailModel detailModel = tmsShipbillDetailMapper.findById(splitGoodsDetailVO.getId(), TmsShipbillDetailModel.class);
                if (ToolKits.isEmpty(detailModel)) {
                    throw new PrologException("运单【" + splitVO.getShipbillId() + "】的商品【" + splitGoodsDetailVO.getId() + "】信息不存在");
                }
                // 取商品资料
                ItemCheckVO itemCheckVO = new ItemCheckVO();
                itemCheckVO.setItemId(detailModel.getItemId());
                RestMessage<ItemCheckDTO> restMessage3 = maindataTmsResource.getItem(itemCheckVO);
                if (!restMessage3.isSuccess()) {
                    throw new RuntimeException("取商品资料信息失败:" + restMessage3.getMessage());
                }
                ItemCheckDTO rsItemInfo = restMessage3.getData();

                BeanUtils.copyProperties(detailModel, subTmsShipbillDetailModel);
                // ID
                subTmsShipbillDetailModel.setId(subShipbillDetailId);
                // 运单ID
                subTmsShipbillDetailModel.setShipbillId(subShipbillId);
                // 运单编号
                subTmsShipbillDetailModel.setShipbillNo(subShipbillId);

                subTmsShipbillDetailModel.setPcs(splitGoodsDetailVO.getPcs());
                subTmsShipbillDetailModel.setWeight(splitGoodsDetailVO.getPcs().multiply(rsItemInfo.getWeight() == null ? BigDecimal.ZERO : rsItemInfo.getWeight()));
                subTmsShipbillDetailModel.setVolume(splitGoodsDetailVO.getPcs().multiply(rsItemInfo.getVolume() == null ? BigDecimal.ZERO : rsItemInfo.getVolume()));
                subTmsShipbillDetailModel.setEnterpriseId(user.getEnterpriseId());
                subTmsShipbillDetailModel.setEnterpriseName(user.getEnterpriseName());

                subTmsShipbillDetailModel.setCreateperson(user.getNickname());
                subTmsShipbillDetailModel.setCreatetime(date);
                subTmsShipbillDetailModel.setUpdateperson(user.getNickname());
                subTmsShipbillDetailModel.setUpdatetime(date);

                subTmsShipbillDetailModel.setOrigPcs(subTmsShipbillDetailModel.getPcs());
                subTmsShipbillDetailModel.setClosingPcs(BigDecimal.valueOf(0));
                subTmsShipbillDetailModel.setDeliveryPcs(BigDecimal.valueOf(0));
                subTmsShipbillDetailModel.setFatherId(detailModel.getId());
                subTmsShipbillDetailModel.setScanStatus(DataDictionaryConstant.SCAN_STATUS_INIT);
                tmsShipbillDetailMapper.save(subTmsShipbillDetailModel);

                goodsTotalPcs = goodsTotalPcs.add(subTmsShipbillDetailModel.getPcs());
                goodsTotalVolume = goodsTotalVolume.add(ToolKits.isEmpty(subTmsShipbillDetailModel.getVolume()) ? BigDecimal.ZERO : subTmsShipbillDetailModel.getVolume());
                goodsTotalWeight = goodsTotalWeight.add(ToolKits.isEmpty(subTmsShipbillDetailModel.getWeight()) ? BigDecimal.ZERO : subTmsShipbillDetailModel.getWeight());
            }

            // 保存拆分子单的运单汇总
            TmsShipbillHeaderModel subTmsShipbillHeaderModel = new TmsShipbillHeaderModel();
            BeanUtils.copyProperties(tmsShipbillHeaderModel, subTmsShipbillHeaderModel);

            subTmsShipbillHeaderModel.setShipbillNo(subShipbillId);
            subTmsShipbillHeaderModel.setShipbillId(subShipbillId);
            if (user.getUserType().equals(BussinessConstant.USER_TYPE_ZY)) {
                // 拆单状态 0未拆单
                subTmsShipbillHeaderModel.setSplitOrderStatus(DataDictionaryConstant.SPLIT_ORDER_STATUS_UN);
                // 拆单类型 3拆分子单
                subTmsShipbillHeaderModel.setSplitOrderType(DataDictionaryConstant.SPLIT_ORDER_TYPE_SPLIT_SUB);

                // 拆单状态 0未拆单
                subTmsShipbillHeaderModel.setCarrierSplitOrderStatus(DataDictionaryConstant.CARRIER_SPLIT_ORDER_STATUS_UN);
                // 拆单类型 3拆分子单
                subTmsShipbillHeaderModel.setCarrierSplitOrderType(DataDictionaryConstant.CARRIER_SPLIT_ORDER_TYPE_UN);
            } else {
                // 拆单状态 0未拆单
                subTmsShipbillHeaderModel.setCarrierSplitOrderStatus(DataDictionaryConstant.CARRIER_SPLIT_ORDER_STATUS_UN);
                // 拆单类型 3拆分子单
                subTmsShipbillHeaderModel.setCarrierSplitOrderType(DataDictionaryConstant.CARRIER_SPLIT_ORDER_TYPE_SPLIT_SUB);

                subTmsShipbillHeaderModel.setSplitOrderStatus(DataDictionaryConstant.SPLIT_ORDER_STATUS_UN);
                // 拆单类型 3拆分子单
                subTmsShipbillHeaderModel.setSplitOrderType(DataDictionaryConstant.SPLIT_ORDER_TYPE_UN);
            }
            // 父级运单ID
            subTmsShipbillHeaderModel.setFatherShipbillId(tmsShipbillHeaderModel.getShipbillId());
            // 父级运单编号
            subTmsShipbillHeaderModel.setFatherShipbillNo(tmsShipbillHeaderModel.getShipbillNo());
            // 原始运单ID
            String originalShipbillId = tmsShipbillHeaderModel.getOriginalShipbillId() == null ? tmsShipbillHeaderModel.getShipbillId() :
                    tmsShipbillHeaderModel.getOriginalShipbillId();
            subTmsShipbillHeaderModel.setOriginalShipbillId(originalShipbillId);
            // 原始运单编号
            String originalShipbillNo = tmsShipbillHeaderModel.getOriginalShipbillNo() == null ? tmsShipbillHeaderModel.getShipbillNo() :
                    tmsShipbillHeaderModel.getOriginalShipbillNo();
            subTmsShipbillHeaderModel.setOriginalShipbillNo(originalShipbillNo);
            // 总件数
            subTmsShipbillHeaderModel.setTotalPcs(goodsTotalPcs);
            // 总体积
            subTmsShipbillHeaderModel.setTotalVolume(goodsTotalVolume);
            // 总重量
            subTmsShipbillHeaderModel.setTotalWeight(goodsTotalWeight);
            // 实际总件数
            subTmsShipbillHeaderModel.setActualTotalPcs(goodsTotalPcs);
            // 实际总体积
            subTmsShipbillHeaderModel.setActualTotalVolume(goodsTotalVolume);
            // 实际总重量
            subTmsShipbillHeaderModel.setActualTotalWeight(goodsTotalWeight);
            // 包裹整件数
            subTmsShipbillHeaderModel.setPackageWholePiece(zero);
            // 包裹拼箱数
            subTmsShipbillHeaderModel.setPackageLclPiece(zero);
            // 包裹总体积
            subTmsShipbillHeaderModel.setPackageTotalVolume(zero);
            // 包裹总重量
            subTmsShipbillHeaderModel.setPackageTotalWeight(zero);

            subTmsShipbillHeaderModel.setCreateperson(user.getNickname());
            subTmsShipbillHeaderModel.setCreatetime(date);
            subTmsShipbillHeaderModel.setUpdateperson(user.getNickname());
            subTmsShipbillHeaderModel.setUpdatetime(date);
            subTmsShipbillHeaderModel.setShipbillType(ShipBillTypeEnum.NORMAL.getCode());
            subTmsShipbillHeaderModel.setSignInStatus(SignInStatusEnum.NOT_SIGN.getCode());
            subTmsShipbillHeaderModel.setDriverReceiveStatus(DriverReceiveStatusEnum.N.getCode());
            subTmsShipbillHeaderModel.setIntelligentLock(IntelligentLockEnum.UN_LOCK.getCode());
            tmsShipbillHeaderMapper.save(subTmsShipbillHeaderModel);

            // 运输记录
            saveTransportLog(subTmsShipbillHeaderModel.getShipbillId(), date, subTmsShipbillHeaderModel.getEstimatedPickupStartTime());

            // 子运单日志保存
            ShipbillOperateLogSaveNewVO subShipbillOperateLogSaveVO = new ShipbillOperateLogSaveNewVO();
            subShipbillOperateLogSaveVO.setShipbillId(subTmsShipbillHeaderModel.getShipbillId());
            subShipbillOperateLogSaveVO.setShipbillNo(subTmsShipbillHeaderModel.getShipbillNo());
            subShipbillOperateLogSaveVO.setOperateEvent(DataDictionaryConstant.SHIPBILL_EVENT_NAME_SPLIT_SUB);
            subShipbillOperateLogSaveVO.setOldStatus(DataDictionaryConstant.SHIPBILL_STATUS_REVIEW);
            subShipbillOperateLogSaveVO.setNewStatus(DataDictionaryConstant.SHIPBILL_STATUS_REVIEW);
            subShipbillOperateLogSaveVO.setOperateDate(date);
            shipbillOperateLogService.addShipbillOperateLogNewData(subShipbillOperateLogSaveVO);

            // 运单节点保存
            ShipbillNodeTrackingSaveNewVO shipbillNodeTrackingSaveVO = new ShipbillNodeTrackingSaveNewVO();
            shipbillNodeTrackingSaveVO.setShipbillId(subShipbillId);
            shipbillNodeTrackingSaveVO.setShipbillNo(subShipbillId);
            shipbillNodeTrackingSaveVO.setStatus(DataDictionaryConstant.SHIPBILL_STATUS_REVIEW);
            shipbillNodeTrackingSaveVO.setNodeEvent(DataDictionaryConstant.SHIPBILL_STATUSNAME_REW);
            shipbillNodeTrackingSaveVO.setNodeDate(date);
            shipbillNodeTrackingService.addShipbillNodeTrackingNewData(shipbillNodeTrackingSaveVO);
            index++;

            if (splitType.equals("disReg")) {
                dispatchShipbills.add(subTmsShipbillHeaderModel.getShipbillId());
            }
        }
        List<String> shipbillIds = Lists.newArrayList();
        shipbillIds.add(splitVO.getShipbillId());
        pendingDispatchService.updatePlanBill(shipbillIds,user);
        return dispatchShipbills;
    }

    /**
     * 生成运输记录表
     */
    public void saveTransportLog(String shipbillId, Date date, Date pickUpStartDt) {
        TmsShipbillTransLogModel logModel = new TmsShipbillTransLogModel();
        RestMessage<String> restMessage = maindataTmsResource.getCodeNumberByCode(SeriaNnumberConstant.TMS_SHIPBILL_TRANSPORT_LOG_ID, "L");
        if (!restMessage.isSuccess()) {
            throw new RuntimeException("生成运输记录ID失败");
        }
        String id = restMessage.getData();
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        logModel.setId(id);
        logModel.setShipbillId(shipbillId);
        logModel.setShipbillNo(shipbillId);

        logModel.setOrderDt(date);
        logModel.setCommitDt(date);
        logModel.setReviewDt(date);

        logModel.setEstimatedPickupStartTime(pickUpStartDt);
        logModel.setEnterpriseId(user.getEnterpriseId());
        logModel.setEnterpriseName(user.getEnterpriseName());
        logModel.setCreateperson(user.getNickname());
        logModel.setCreatetime(date);
        logModel.setUpdateperson(user.getNickname());
        logModel.setUpdatetime(date);

        tmsShipbillTransportLogMapper.save(logModel);
    }
}
