# SorterManagerImpl.java

    package com.mycim.server.sorter.manager.imp;

    import com.fa.sesa.exception.Assert;
    import com.fa.sesa.exception.Errors;
    import com.fa.sesa.exception.SystemIllegalArgumentException;
    import com.fa.sesa.threadlocal.LocalContext;
    import com.mycim.framework.logging.Logger;
    import com.mycim.framework.logging.LoggerFactory;
    import com.mycim.framework.oid.IDGenerators;
    import com.mycim.framework.oid.type.IDNames;
    import com.mycim.framework.oid.type.SequenceNames;
    import com.mycim.framework.utils.beans.BeanUtils;
    import com.mycim.framework.utils.lang.StringUtils;
    import com.mycim.framework.utils.lang.collections.CollectionUtils;
    import com.mycim.framework.utils.lang.collections.MapUtils;
    import com.mycim.framework.utils.lang.math.NumberUtils;
    import com.mycim.server.asm.manager.MaterialManager;
    import com.mycim.server.automonitor.manager.LotAutoMonitorInqManager;
    import com.mycim.server.automonitor.manager.LotAutoMonitorReqManager;
    import com.mycim.server.base.manager.NamedObjectManager;
    import com.mycim.server.base.manager.TransactionLogManager;
    import com.mycim.server.carrier.manager.CarrierManager;
    import com.mycim.server.carrier.manager.PcdManager;
    import com.mycim.server.sorter.dao.SorterDAO;
    import com.mycim.server.sorter.dao.SorterQueryDAO;
    import com.mycim.server.sorter.manager.SorterManager;
    import com.mycim.server.sorter.manager.SorterQueryManager;
    import com.mycim.server.spec.manager.ProcessSpecItemManager;
    import com.mycim.server.system.manager.ReferenceFileManager;
    import com.mycim.server.wip.manager.*;
    import com.mycim.valueobject.MessageIdList;
    import com.mycim.valueobject.ObjectList;
    import com.mycim.valueobject.SystemConstant;
    import com.mycim.valueobject.automonitor.entity.LotAutoMonitorInfo;
    import com.mycim.valueobject.automonitor.entity.MonitorCarrierMapping;
    import com.mycim.valueobject.automonitor.util.AutoMonitorUtils;
    import com.mycim.valueobject.bas.NamedObject;
    import com.mycim.valueobject.bas.TransactionLog;
    import com.mycim.valueobject.consts.*;
    import com.mycim.valueobject.ems.Carrier;
    import com.mycim.valueobject.inv.MaterialDO;
    import com.mycim.valueobject.runcard.dto.RunCardMergeInfoDTO;
    import com.mycim.valueobject.runcard.dto.RunCardSplitInfoDTO;
    import com.mycim.valueobject.runcard.util.RunCardConstants;
    import com.mycim.valueobject.runcard.util.RunCardUtils;
    import com.mycim.valueobject.sorter.*;
    import com.mycim.valueobject.wip.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;

    import java.util.*;
    import java.util.stream.Collectors;

    import static java.util.stream.Collectors.collectingAndThen;
    import static java.util.stream.Collectors.toCollection;

    /**
     * @author yibing.liu
     * @version 1.0
     * @date 2021/06/29
     */
    @Service
    @Transactional
    public class SorterManagerImpl implements SorterManager {

        @Autowired
        SorterDAO sorterDAO;

        @Autowired
        SorterQueryDAO sorterQueryDAO;

        @Autowired
        NamedObjectManager namedObjectManager;

        @Autowired
        TransactionLogManager transactionLogManager;

        @Autowired
        LotManager lotManager;

        @Autowired
        LotQueryManager lotQueryManager;

        @Autowired
        LotInqManager lotInqManager;

        @Autowired
        LotRunCardManager lotRunCardManager;

        @Autowired
        RunCardQueryManager runCardQueryManager;

        @Autowired
        UnitQueryManager unitQueryManager;

        @Autowired
        CarrierManager carrierManager;

        @Autowired
        SorterManager sorterManager;

        @Autowired
        SorterQueryManager sorterQueryManager;

        @Autowired
        LotAutoMonitorInqManager lotAutoMonitorInqManager;

        @Autowired
        LotAutoMonitorReqManager lotAutoMonitorReqManager;

        @Autowired
        ProcessSpecItemManager processSpecItemManager;

        @Autowired
        UnitManager unitManager;

        @Autowired
        WipQueryManager wipQueryManager;

        @Autowired
        ReferenceFileManager referenceFileManager;

        @Autowired
        PcdManager pcdManager;

        @Autowired
        LotTransHistoryReqManager lotTransHistoryReqManager;

        @Autowired
        MaterialManager materialManager;

        private Logger logger = LoggerFactory.getLogger(SorterManagerImpl.class);

        @Override
        public void addSortJob(SortJobBean sortJobBean, TransactionLog tl) {

            //1 先分配主任务的 mainJobRrn
            long mainJobRrn = Long.parseLong(
                    IDGenerators.get(IDNames.SEQUENCE).generateId(SequenceNames.MODULE_SEQ_OBJECT_RRN));
            for (SorterBean sorterBean : sortJobBean.getSorterBeans()) {
                Assert.isFalse(CollectionUtils.isEmpty(sorterBean.getSorterDetailBeanList()),
                               Errors.create().key(MessageIdList.UNIT_DATA_ERROR).build());
                sorterBean.setMainJobRrn(mainJobRrn);
                long subJobRrn = Long.parseLong(
                        IDGenerators.get(IDNames.SEQUENCE).generateId(SequenceNames.MODULE_SEQ_OBJECT_RRN));
                sorterBean.setSubJobRrn(subJobRrn);
                sorterBean.setCreateTime(tl.getTransStartTimestamp());
                //2 在分配子任务的 subJobRrn ,一个mainJobRrn对应一个subJobRrn,也可能对应对个subJobRrn 比如：多PORT和multiLot
                sorterDAO.addSorter(sorterBean, tl.getTransRrn());
                //3 添加创建时的sorter carrier mapping 记录。
                insertSorterCarrierMap(sorterBean);
            }

        }

        private void insertSorterCarrierMap(SorterBean sorterBean) {
            List<SorterCarrierMap> carrierMapList=new ArrayList<>();
            carrierMapList.addAll(buildSourceSorterCarrierMap(sorterBean));
            carrierMapList.addAll(buildTargetSorterCarrierMap(sorterBean));
            sorterDAO.insertSorterCarrierMapList(carrierMapList);
        }

        private List<SorterCarrierMap> buildSourceSorterCarrierMap(SorterBean sorterBean) {
            Long carrierMapRrn = sorterBean.getSourceCarrierMapRrn();
            Long carrierRrn = sorterBean.getSourceCarrierRrn();
            String carrierId = sorterBean.getSourceCarrierId();
            return buildSourceSorterCarrierMap(sorterBean, carrierMapRrn, carrierRrn, carrierId);
        }

        private List<SorterCarrierMap> buildTargetSorterCarrierMap(SorterBean sorterBean) {
            Long carrierMapRrn = sorterBean.getTargetCarrierMapRrn();
            Long carrierRrn = sorterBean.getTargetCarrierRrn();
            String carrierId = sorterBean.getTargetCarrierId();
            return buildSourceSorterCarrierMap(sorterBean, carrierMapRrn, carrierRrn, carrierId);
        }

        private List<SorterCarrierMap> buildSourceSorterCarrierMap(SorterBean sorterBean, Long carrierMapRrn,
                                                                   Long carrierRrn, String carrierId) {
            List<SorterCarrierMap> sorterCarrierMapList = new ArrayList<>();
            List<Map<String, Object>> list = unitQueryManager.getUnitListByCarrierMapRrn(carrierMapRrn);
            for (Map<String, Object> map : list) {
                SorterCarrierMap carrierMap = new SorterCarrierMap();
                carrierMap.setMainJobRrn(sorterBean.getMainJobRrn());
                carrierMap.setSubJobRrn(sorterBean.getSubJobRrn());
                carrierMap.setCarrierMapRrn(carrierMapRrn);
                carrierMap.setCarrierRrn(carrierRrn);
                carrierMap.setCarrierId(carrierId);
                carrierMap.setLotRrn(MapUtils.getLong(map, "lotRrn"));
                carrierMap.setLotId(MapUtils.getString(map, "lotid"));
                carrierMap.setUnitRrn(MapUtils.getLong(map, "unitRrn"));
                carrierMap.setUnitId(MapUtils.getString(map, "unitId"));
                carrierMap.setPosition(MapUtils.getInteger(map, "position"));

                sorterCarrierMapList.add(carrierMap);
            }
            return sorterCarrierMapList;
        }

        @Override
        public void addSortJobList(List<SortJobBean> sortJobBeanList, TransactionLog tl) {
            for (SortJobBean sortJobBean : sortJobBeanList) {
                addSortJob(sortJobBean, tl);
            }
        }

        @Override
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public void updateSorter(SortJobBean sortJob) {
            sorterDAO.updateSorter(sortJob);
        }

        @Override
        public void deleteSorter(SorterBean sorterBean, TransactionLog tl) {

            sorterBean.setUpdateTime(tl.getTransStartTimestamp());
            sorterBean.setUpdateUser(LocalContext.getUserId());
            //先检查一下此Sort任务的状态是否已经被修改
            List<SorterBean> sorterBeans = checkSorterTaskStatus(sorterBean);

            if (SorterEnum.Constant.CREATE_MONITOR.equalsIgnoreCase(SorterEnum.getListFirst(sorterBeans).getJobType())) {
                int finishTask = 0;
                for (SorterBean s : sorterBeans) {
                    if (SorterEnum.Constant.FINISH.equalsIgnoreCase(s.getStatus())) {
                        finishTask++;
                    }
                }
                if (finishTask > 0) {
                    //CreateMonitor 的 SortJob 已经执行了一部分，因此不能取消剩余的 SortTask。
                    throw new SystemIllegalArgumentException(
                            Errors.create().key(MessageIdList.SORT_JOB_CAN_NOT_CANCEL).build());
                }
            }
            sorterDAO.deleteSorterByJobRrn(sorterBean, tl.getTransRrn());

        }

        @Override
        public String endSortJob(SortJobBean sortJobBean) {
            TransactionLog transactionLog = transactionLogManager.startTransactionLog(sortJobBean.getTransPerformedby(),
                                                                                      sortJobBean.getTransId());
            //1 处理 JobType 对应的业务操作
            SortJobBean sortJob = sorterQueryDAO.getSortJob(sortJobBean);//通过晶舟的RRN去查
            Assert.isFalse(CollectionUtils.isEmpty(sortJob.getSorterBeans()),
                           Errors.create().key(MessageIdList.NO_SORT_JOB).build());
            String msg = "";
            try {
                sortJob.setEquipmentId(sortJobBean.getEquipmentId());
                msg = continueDealJob(sortJob);
                //2 修改 SORT_JOB 、 删除 SORT_JOB_DETAIL
                sortJob.setLastUpdateTimestamp(transactionLog.getTransStartTimestamp());
                sortJob.setLastUpdateUserId(LocalContext.getUserId());
                sorterDAO.endSorter(sortJob, transactionLog.getTransRrn());
                transactionLogManager.markTransactionLog(transactionLog);
            } catch (Exception e) {
                sortJob.setStatus(SorterEnum.Status.DISPATCH.getStatus());
                sorterManager.updateSorter(sortJob);
                logger.error(e);
                throw new SystemIllegalArgumentException(Errors.create().content(e.getMessage()).build());
            }
            return msg;
        }

        @Override
        public void manualSortJob(SorterBean sorterBean) {
            //先检查一下此Sort任务的状态是否已经被修改
            checkSorterTaskStatus(sorterBean);
            sorterBean.setTransPerformedby(LocalContext.getUserId());
            SortJobBean sjb = new SortJobBean();
            //multiLot时，需要SubJobRRN
            sjb.setMainJobRrn(sorterBean.getMainJobRrn());
            sjb.setTransId(SorterEnum.Constant.MANUAL_END_SORTER);
            endSortJob(sjb);
        }

        @Override
        public void manualAutoMonitorSorterJob(SorterBean sorterBean) {
            //先检查一下此Sort任务的状态是否已经被修改
            checkSorterTaskStatus(sorterBean);
            sorterBean.setTransPerformedby(LocalContext.getUserId());
            SortJobBean sjb = new SortJobBean();
            //multiLot时，需要SubJobRRN
            sjb.setMainJobRrn(sorterBean.getMainJobRrn());
            sjb.setTransId(SorterEnum.Constant.MANUAL_AUTO_MONITOR_SORTER);
            endSortJob(sjb);
        }

        @Override
        public void synAutoMonitorSortJob(Long sourceCarrierRrn, Long monitorCarrierRrn) {
            //1. 检查预设关系
            List<MonitorCarrierMapping> carrierMappings = lotAutoMonitorInqManager
                    .getMonitorCarrierMappings(monitorCarrierRrn);

            // 预设关系中的 lot rrn
            List<Long> lotRrnList = carrierMappings.stream().map(MonitorCarrierMapping::getLotRrn)
                                                   .collect(Collectors.toList()).stream().distinct()
                                                   .collect(Collectors.toList());

            //2. 检查预设批次状态
            List<Long> createSorterLotRrn = new ArrayList<>();
            Boolean canCreateSorter = Boolean.TRUE;
            for (Long lotRrn : lotRrnList) {
                LotAutoMonitorInfo autoInfo = lotAutoMonitorInqManager.getLotAutoMonitorInfo(lotRrn);
                // 确认还未进入mcst
                if (autoInfo.getCarrierRrn().longValue() == sourceCarrierRrn) {
                    if (lotAutoMonitorInqManager.checkAutoMonitorLotCanCreateSortJob(autoInfo)) {
                        createSorterLotRrn.add(lotRrn);
                    } else {
                        canCreateSorter = Boolean.FALSE;
                        break;
                    }
                }
            }
            if (canCreateSorter && CollectionUtils.isEmpty(createSorterLotRrn)) {
                canCreateSorter = Boolean.FALSE;
            }

            //3. 删除已有sorter
            TransactionLog transactionLog = transactionLogManager
                    .startTransactionLog(LocalContext.getUserId(), SorterEnum.Constant.SYN_MONITOR_SORTER);

            List<SorterBean> sorterList = sorterQueryManager.getSoterListByCarrier(sourceCarrierRrn, monitorCarrierRrn);
            if (CollectionUtils.isNotEmpty(sorterList)) {
                for (SorterBean sorterBean : sorterList) {
                    sorterDAO.deleteSorterByJobRrn(sorterBean, transactionLog.getTransRrn());
                }
            }

            //4. 创建sorter
            if(canCreateSorter) {
                // 检查状态
                Assert.state(CollectionUtils.isEmpty(unitQueryManager.getUnitListByCarrier(monitorCarrierRrn)),
                             Errors.create().key(MessageIdList.TARGET_BE_USED).build());

                List<SorterDetailBean> detailList = new ArrayList<>();
                for (MonitorCarrierMapping carrierMapping : carrierMappings) {
                    if(createSorterLotRrn.contains(carrierMapping.getLotRrn())) {
                        SorterDetailBean sorterDetailBean = new SorterDetailBean();
                        BeanUtils.copyProperties(carrierMapping, sorterDetailBean);
                        sorterDetailBean.setSourcePosition(carrierMapping.getSourcePosition());
                        sorterDetailBean.setTargetPosition(carrierMapping.getPosition());
                        detailList.add(sorterDetailBean);
                    }
                }

                if (CollectionUtils.isEmpty(detailList)) {
                    transactionLogManager.markTransactionLog(transactionLog);
                    return;
                }

                SorterBean sorterBean = buildSorterBean(sourceCarrierRrn, monitorCarrierRrn, detailList);
                sorterBean.setJobType(SorterEnum.Constant.AUTO_MONITOR);
                sorterBean.setCreateTime(transactionLog.getTransStartTimestamp());
                sorterBean.setCreateUser(LocalContext.getUserId());
                SortJobBean sortJobBean = new SortJobBean();
                sortJobBean.setSorterBeans(sorterBean);
                sortJobBean.setTransPerformedby(LocalContext.getUserId());
                addSortJob(sortJobBean, transactionLog);
            }
            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public void addAutoMonitorMergeSortJob(Long targetCarrierRrn, Long monitorCarrierRrn) {
            List<Lot> monitorLotList = lotInqManager.getLotListByCarrierRrn(monitorCarrierRrn);
            addAutoMonitorMergeSortJob(monitorLotList, targetCarrierRrn, monitorCarrierRrn);
        }

        @Override
        public void addAutoMonitorMergeSortJob(List<Lot> lotList, Long targetCarrierRrn, Long monitorCarrierRrn) {
            String userId = LocalContext.getUserId();

            List<MonitorCarrierMapping> carrierMappings = lotAutoMonitorInqManager
                    .getMonitorCarrierMappings(monitorCarrierRrn);

            List<SorterDetailBean> detailList = new ArrayList<>();
            for (Lot lot : lotList) {
                Assert.state(!LotStatus.isProcessingStatus(lot.getLotStatus()),
                             Errors.create().key(MessageIdList.EQUIPMENT_CHECK_STATUS).args(lot.getLotId()).build());

                for (MonitorCarrierMapping carrierMapping : carrierMappings) {
                    if (lot.getLotRrn() == carrierMapping.getLotRrn().longValue()) {
                        SorterDetailBean sorterDetailBean = new SorterDetailBean();
                        BeanUtils.copyProperties(carrierMapping, sorterDetailBean);
                        sorterDetailBean.setSourcePosition(carrierMapping.getPosition());
                        sorterDetailBean.setTargetPosition(carrierMapping.getSourcePosition());
                        detailList.add(sorterDetailBean);
                    }
                }
            }

            TransactionLog tl = transactionLogManager.startTransactionLog(userId, SorterEnum.Constant.CREATE_SORTER);

            SorterBean sorterBean = buildSorterBean(monitorCarrierRrn, targetCarrierRrn, detailList);
            sorterBean.setJobType(SorterEnum.Constant.AUTO_MONITOR_MERGE);
            sorterBean.setCreateTime(tl.getTransStartTimestamp());
            sorterBean.setCreateUser(LocalContext.getUserId());
            SortJobBean sortJobBean = new SortJobBean();
            sortJobBean.setSorterBeans(sorterBean);
            sortJobBean.setTransPerformedby(userId);

            sorterManager.addSortJob(sortJobBean, tl);
            transactionLogManager.markTransactionLog(tl);
        }

        @Override
        public void autoCancelAutoMonitorSortJob(Long sourceCarrierRrn) {
            List<SorterBean> sorterList = sorterQueryManager.getSoterListByCarrier(sourceCarrierRrn);
            if (CollectionUtils.isNotEmpty(sorterList)) {
                TransactionLog transactionLog = transactionLogManager.startTransactionLog(LocalContext.getUserId(),
                                                                                          SorterEnum.Constant.CANCEL_SORTER);

                for (SorterBean sorterBean : sorterList) {
                    sorterDAO.deleteSorterByJobRrn(sorterBean, transactionLog.getTransRrn());
                }

                transactionLogManager.markTransactionLog(transactionLog);
            }
        }

        @Override
        public List<SorterBean> checkSorterTaskStatus(SorterBean sorterBean) {
            List<SorterBean> sorterBeans = sorterQueryDAO.getJobStatusList(sorterBean);
            boolean canDo = false;
            for (SorterBean s : sorterBeans) {
                if (StringUtils.equalsIgnoreCase(s.getStatus(), SorterEnum.Status.CREATE.getStatus()) ||
                        StringUtils.equalsIgnoreCase(s.getStatus(), SorterEnum.Status.DISPATCH.getStatus())) {
                    canDo = true;
                }
            }

            if (!canDo) { //没有可执行的子任务
                throw new SystemIllegalArgumentException(Errors.create().key(MessageIdList.REFRESH_AGAIN).build());
            }
            return sorterBeans;
        }

        @Override
        public void assignTargetCarriers(long sourceCarrierRrn, SorterBean assignSorterBean,
                                         TransactionLog transactionLog) {
            SorterBean sbStr = new SorterBean();
            sbStr.setMainJobRrn(assignSorterBean.getMainJobRrn());
            sbStr.setSubJobRrn(assignSorterBean.getSubJobRrn());
            sbStr.setTargetCarrierId(assignSorterBean.getTargetCarrierId());
            sbStr.setTargetCarrierRrn(assignSorterBean.getTargetCarrierRrn());
            sorterDAO.updateSorterBean(sbStr);
            transactionLog.setComments(Long.toString(assignSorterBean.getSubJobRrn()));//记录修改的sub_job_rrn，以便以后关联排查记录。
        }

        @Override
        public void createSorterJobByInLine(SorterModel sorterModel, Lot lot, String targetCarrierId) {
            SortJobBean queryBean = sorterQueryManager.querySortJobByCarrierRrn(lot.getCarrierRrn());
            if (queryBean.getMainJobRrn() == 0) {
                String transPerformedBy = SystemConstants.SYSTEM_USER;
                TransactionLog tl = transactionLogManager.startTransactionLog(transPerformedBy,
                                                                              SorterEnum.Constant.CREATE_SORTER);
                SortJobBean sortJobBean = new SortJobBean();
                if (SorterEnum.Constant.EXCHANGE.equalsIgnoreCase(sorterModel.getJobType())) {
                    sortJobBean = buildSorterJobBeanForExchange(sorterModel, lot, targetCarrierId);
                }else {
                    // 根据Model 构建数据
                    sortJobBean = buildSorterJobBeanByModel(sorterModel, lot);
                }
                // 添加数据
                addSortJob(sortJobBean, tl);
                transactionLogManager.markTransactionLog(tl);
            } else if (queryBean.getMainJobRrn() > 0) {
                // check 是否已经存在Offline 类型的Job
                queryBean.getSorterBeans().forEach(job -> {
                    Assert.state(StringUtils.equalsIgnoreCase(job.getSorterType(), SorterEnum.Type.OFFLINE.getName()),
                                 Errors.create().key(MessageIdList.SORTER_CST_HAVE_JOB)
                                       .content("Current CST has a Offline Sorter Job, Complete the Job first").build());
                });
            }

        }

        private SortJobBean buildSorterJobBeanByModel(SorterModel sorterModel, Lot lot) {
            // 获取Lot的Carrier
            Long sourceCarrierRrn = lot.getCarrierRrn();

            // 构建基础数据
            SorterBean sorterBean = buildSorterBean(sourceCarrierRrn, sourceCarrierRrn, new ArrayList<>());
            sorterBean.setSorterType(SorterEnum.Type.INLINE.getName());
            sorterBean.setJobType(sorterModel.getJobType());
            sorterBean.setCreateUser(SystemConstants.SYSTEM_USER);

            //根据不同类型构建特殊数据 预留扩展
            switch (sorterModel.getJobType()) {
                case SorterEnum.Constant.FLIP_SIDE:
                    buildFlipSideSortInfo(sorterBean, lot);
                    break;
                case SorterEnum.Constant.READ_T7CODE:
                    buildReadT7CodeSortInfo(sorterBean, lot);
                    break;
                default:
                    Assert.isTrue(true, Errors.create().content("Job Type Error").build());
            }

            SortJobBean sortJobBean = new SortJobBean();
            sortJobBean.setSorterBeans(sorterBean);
            return sortJobBean;
        }

        private SortJobBean buildSorterJobBeanForExchange(SorterModel sorterModel, Lot lot, String targetCarrierId) {
            Long sourceCarrierRrn = lot.getCarrierRrn();
            Long targetCarrierRrn = null;
            if (StringUtils.isNotBlank(targetCarrierId)) {
                Carrier carrier = carrierManager.getCarrier(LocalContext.getFacilityRrn(), targetCarrierId);
                if (carrier != null && carrier.getInstanceRrn() > 0) {
                    targetCarrierRrn = carrier.getInstanceRrn();
                }
            }
            // 构建基础数据
            SorterBean sorterBean = buildSorterBean(sourceCarrierRrn, targetCarrierRrn, new ArrayList<>());
            sorterBean.setSorterType(SorterEnum.Type.INLINE.getName());
            sorterBean.setJobType(sorterModel.getJobType());
            sorterBean.setCreateUser(SystemConstants.SYSTEM_USER);

            // 构建units
            List<Map> sourceUnits = unitQueryManager.getUnitListByCarrier(lot.getCarrierRrn());
            List<SorterDetailBean> sourceUnitList = sourceUnits.stream().map(unitMap -> {
                String unitId = MapUtils.getString(unitMap, "unitId");
                long unitRrn = MapUtils.getLong(unitMap, "unitRrn", 0L);
                String lotId = MapUtils.getString(unitMap, "lotId");
                long lotRrn = MapUtils.getLong(unitMap, "lotRrn", 0L);
                int position = MapUtils.getIntValue(unitMap, "position", 0);
                return new SorterDetailBean(lotRrn, lotId, unitRrn, unitId, position, position);
            }).collect(Collectors.toList());
            sorterBean.setSorterDetailBeanList(sourceUnitList);
            sorterBean.setExchangeTotalQty(sourceUnits.size());
            SortJobBean sortJobBean = new SortJobBean();
            sortJobBean.setSorterBeans(sorterBean);
            return sortJobBean;
        }

        private void buildFlipSideSortInfo(SorterBean sorterBean, Lot lot) {
            String flipType = sorterQueryManager.getFlipTypeByLot(lot);

            sorterBean.setJsonAttributeData3(StringUtils.replace(flipType, "-", "|"));
            // 构建units
            List<Map> sourceUnits = unitQueryManager.getUnitListByCarrier(lot.getCarrierRrn());
            List<SorterDetailBean> sourceUnitList = sourceUnits.stream().map(unitMap -> {
                String unitId = MapUtils.getString(unitMap, "unitId");
                long unitRrn = MapUtils.getLong(unitMap, "unitRrn", 0L);
                String lotId = MapUtils.getString(unitMap, "lotId");
                long lotRrn = MapUtils.getLong(unitMap, "lotRrn", 0L);
                int position = MapUtils.getIntValue(unitMap, "position", 0);
                return new SorterDetailBean(lotRrn, lotId, unitRrn, unitId, position, position);
            }).collect(Collectors.toList());
            sorterBean.setSorterDetailBeanList(sourceUnitList);
            sorterBean.setExchangeTotalQty(sourceUnits.size());
        }

        private SorterBean buildSorterBean(Long sourceCarrierRrn, Long targetCarrierRrn, List<SorterDetailBean> unitList) {

            Carrier sourceCarrier = carrierManager.getCarrier(sourceCarrierRrn);
            Carrier targetCarrier = null;
            if (targetCarrierRrn != null && targetCarrierRrn > 0) {
                targetCarrier = carrierManager.getCarrier(targetCarrierRrn);
            }
            SorterBean sorterBean = new SorterBean();
            sorterBean.setSourceCarrierId(sourceCarrier.getInstanceId());
            sorterBean.setSourceCarrierRrn(sourceCarrier.getInstanceRrn());
            sorterBean.setSourceCarrierMapRrn(sourceCarrier.getCarrierMapRrn());
            sorterBean.setTargetCarrierId(targetCarrier == null ? StringUtils.EMPTY : targetCarrier.getInstanceId());
            sorterBean.setTargetCarrierRrn(targetCarrier == null ? NumberUtils.LONG_ZERO : targetCarrier.getInstanceRrn());
            sorterBean.setTargetCarrierMapRrn(targetCarrier == null ? NumberUtils.LONG_ZERO : targetCarrier.getCarrierMapRrn());
            sorterBean.setStatus(SorterEnum.Constant.CREATE);
            sorterBean.setCreateUser(LocalContext.getUserId());
            sorterBean.setExchangeTotalQty(unitList.size());
            sorterBean.setSorterDetailBeanList(unitList);
            sorterBean.setJsonAttributeData5(StringUtils.EMPTY + sourceCarrier.getCarrierMapRrn());
            return sorterBean;
        }

        private String continueDealJob(SortJobBean sortJobBean) {
            NamedObject eqpt = namedObjectManager.getNamedObject(sortJobBean.getEquipmentId(),
                                                                 namedObjectManager.getNamedSpace(
                                                                         LocalContext.getFacilityRrn(),
                                                                         ObjectList.ENTITY_KEY), ObjectList.ENTITY_KEY);
            sortJobBean.setEquipmentRrn(Objects.isNull(eqpt) ? -1L : eqpt.getInstanceRrn());
            String message = SorterEnum.HardCode.SUCCESS;
            switch (sortJobBean.getJobType().toUpperCase()) {
                case SorterEnum.Constant.MERGE:
                    merge(sortJobBean);
                    break;
                case SorterEnum.Constant.SPLIT:
                    splitLot(sortJobBean, SorterEnum.Constant.SPLIT);
                    break;
                case SorterEnum.Constant.EXCHANGE:
                    exchangeCarrier(sortJobBean, SorterEnum.Constant.EXCHANGE);
                    break;
                case SorterEnum.Constant.SPLIT_3PORT:
                    splitTo3Port(sortJobBean);
                    break;
                case SorterEnum.Constant.SRC_SPLIT:
                    runCardSplit(sortJobBean);
                    break;
                case SorterEnum.Constant.SRC_MERGE:
                    runCardMerge(sortJobBean);
                    break;
                case SorterEnum.Constant.AUTO_MONITOR:
                    autoMonitorExchange(sortJobBean);
                    break;
                case SorterEnum.Constant.AUTO_MONITOR_MERGE:
                    autoMonitorMergeCarrier(sortJobBean);
                    break;
                case SorterEnum.Constant.FLIP_SIDE:
                    flipSideCarrier(sortJobBean);
                    break;
                case SorterEnum.Constant.PILOT_SPLIT:
                    splitLot(sortJobBean, SorterEnum.Constant.PILOT_SPLIT);
                    break;
                case SorterEnum.Constant.SPECIAL_EXCHANGE:
                    exchangeCarrier(sortJobBean, SorterEnum.Constant.SPECIAL_EXCHANGE);
                    break;
                case SorterEnum.Constant.SRC_EXCHANGE:
                    srcExchangeCarrier(sortJobBean, SorterEnum.Constant.SRC_EXCHANGE);
                    break;
                case SorterEnum.Constant.SRC_SPECIAL_EXCHANGE:
                    srcExchangeCarrier(sortJobBean, SorterEnum.Constant.SRC_SPECIAL_EXCHANGE);
                    break;
                case SorterEnum.Constant.OFFLINE_CHECK_T7CODE:
                    recordSortLotTrans(sortJobBean, TransactionNames.OFFLINE_CHECK_T7CODE);
                    break;
                case SorterEnum.Constant.OFFLINE_READ_MATERIALID:
                    recordSortLotTrans(sortJobBean, TransactionNames.OFFLINE_READ_MATERIALID);
                    break;
                default:
                    return null;
            }
            return message;
        }

        //TODO
        private void srcExchangeCarrier(SortJobBean sortJobBean, String srcExchangeType) {
            long facilityRrn = LocalContext.getFacilityRrn();
            List<SorterBean> sorterBeans = sortJobBean.getSorterBeans();
            SorterBean sorterBean = SorterEnum.getListFirst(sorterBeans);
            List<SorterDetailBean> sorterDetailBeans = sorterBean.getSorterDetailBeanList();

            Lot lot = lotQueryManager.getLotByCarrierRrn(sorterBean.getSourceCarrierRrn());//SRC 的LOT
            if (LotStatus.isRunCardHold(lot.getLotStatus())) {//MAINRC
                lot = lotQueryManager.getLot(RunCardUtils.buildMainRcLotId(lot.getLotId()),
                                             lot.getFacilityRrn());
            }

            // List<Unit> parentUnitList = unitQueryManager.getUnitList(lot.getLotRrn());

            lot.setLotComments(
                    (SorterEnum.Constant.SRC_EXCHANGE.equalsIgnoreCase(srcExchangeType) ? "普通" : "特殊") + "交换晶舟，初始晶舟为： " +
                            sorterBean.getSourceCarrierId() + ", SortJob Finish. ");
            lot.setTransPerformedby(sorterBean.getCreateUser());
            lot.setTransId(srcExchangeType);
            lot.setSorterFlag(true);
            lot.setLocation(sortJobBean.getEquipmentId());
            lot.setEqptRrn(sortJobBean.getEquipmentRrn());

            Carrier toCarrier = carrierManager.getCarrier(sorterBean.getTargetCarrierRrn());
            //carrier的一些校验
            Assert.isFalse(!StringUtils.equalsIgnoreCase(toCarrier.getCarrierStatus(), PcdStatus.FREE_KEY) &&
                                   !StringUtils.equalsIgnoreCase(toCarrier.getCarrierStatus(), PcdStatus.ASSEMBLY_KEY),
                           Errors.create().key(MessageIdList.CARRIER_STATUS_INVALID).args(toCarrier.getInstanceId())
                                 .build());

            List<Map> carrierMappings = buildCarrierMapping(sorterDetailBeans);
            carrierManager.exchangeCarrierSpecial4Src(lot, toCarrier.getInstanceRrn(), carrierMappings, toCarrier, facilityRrn,
                                                sorterBean.getCreateUser());
        }
        private void recordSortLotTrans(SortJobBean sortJobBean, String transName) {
            //无需做其他处理，只需记录trans
            TransactionLog transactionLog = transactionLogManager
                    .startTransactionLog(LocalContext.getUserId(), transName);
            List<SorterBean> sorterBeans = sortJobBean.getSorterBeans();
            SorterBean firstSorterBean = SorterEnum.getListFirst(sorterBeans);
            List<Lot> lotListByCarrierRrn = lotInqManager.getLotListByCarrierRrn(firstSorterBean.getSourceCarrierRrn());
            if (CollectionUtils.size(lotListByCarrierRrn) > 1 || CollectionUtils.isEmpty(lotListByCarrierRrn)) {
                return;
            }
            Lot lot = lotListByCarrierRrn.get(0);
            if (Objects.isNull(lot)) {
                return;
            }
            if (LotStatus.isRunCardHold(lot.getLotStatus())) {
                lot = lotInqManager.getLot(RunCardUtils.buildMainRcLotId(lot.getLotId()));
            }
            String transComments = sortJobBean.getJobType() + "，SortJob Finish。";
            lotTransHistoryReqManager.createLotTransHistory(transactionLog, lot, transComments);

            transactionLogManager.markTransactionLog(transactionLog);
        }

        private void autoMonitorExchange(SortJobBean sortJobBean) {
            SorterBean sb = SorterEnum.getListFirst(sortJobBean.getSorterBeans());

            TransactionLog transactionLog = transactionLogManager.startTransactionLog(LocalContext.getUserId(),
                                                                                      TransactionNames.AUTO_MONITOR_EXCHANGE);

            Long sourceCarrierRrn = sortJobBean.getSourceCarrierRrn();
            Long targetCarrierRrn = sortJobBean.getTargetCarrierRrn();
            List<Long> exchangeLotRrnList = new ArrayList<>();
            for(SorterDetailBean sorterDetailBean:  sb.getSorterDetailBeanList()) {
                if(!exchangeLotRrnList.contains(sorterDetailBean.getLotRrn())) {
                    exchangeLotRrnList.add(sorterDetailBean.getLotRrn());
                }
            }

            for (Long lotRrn : exchangeLotRrnList) {
                LotAutoMonitorInfo monitorInfo = lotAutoMonitorInqManager.getLotActiveAutoMonitorInfo(lotRrn);
                if (monitorInfo != null && monitorInfo.getMonitorCarrierRrn() != null) {
                    monitorInfo.setBySort(true);
                    monitorInfo.setSorterEqptId(sortJobBean.getEquipmentId());
                    monitorInfo.setSorterEqptRrn(sortJobBean.getEquipmentRrn());
                    lotAutoMonitorReqManager.exchangeAutoMonitorLotCarrier(transactionLog, monitorInfo, targetCarrierRrn);
                }
            }

            buildCarrierMappingInfo(transactionLog, sourceCarrierRrn);
            buildCarrierMappingInfo(transactionLog, targetCarrierRrn);

            SorterBean temp = new SorterBean(sb.getMainJobRrn());
            temp.setSubJobRrn(sb.getSubJobRrn());
            sorterDAO.updateSorterBean(temp);//回填子批ID

            transactionLogManager.markTransactionLog(transactionLog);
        }

        private void autoMonitorMergeCarrier(SortJobBean sortJobBean) {
            SorterBean sb = SorterEnum.getListFirst(sortJobBean.getSorterBeans());

            TransactionLog transactionLog = transactionLogManager.startTransactionLog(LocalContext.getUserId(),
                                                                                      TransactionNames.AUTO_MONITOR_MERGE);

            Long sourceCarrierRrn = sortJobBean.getSourceCarrierRrn();
            Long targetCarrierRrn = sortJobBean.getTargetCarrierRrn();

            List<Long> exchangeLotRrnList = new ArrayList<>();
            for(SorterDetailBean sorterDetailBean:  sb.getSorterDetailBeanList()) {
                if(!exchangeLotRrnList.contains(sorterDetailBean.getLotRrn())) {
                    exchangeLotRrnList.add(sorterDetailBean.getLotRrn());
                }
            }

            for(Long exchangeLotRrn:exchangeLotRrnList) {
                LotAutoMonitorInfo monitorInfo = lotAutoMonitorInqManager.getLotActiveAutoMonitorInfo(exchangeLotRrn);
                if (monitorInfo != null && monitorInfo.getMonitorCarrierRrn() != null &&
                        monitorInfo.getCarrierRrn().longValue() == monitorInfo.getMonitorCarrierRrn().longValue()) {
                    monitorInfo.setBySort(true);
                    monitorInfo.setSorterEqptId(sortJobBean.getEquipmentId());
                    monitorInfo.setSorterEqptRrn(sortJobBean.getEquipmentRrn());
                    lotAutoMonitorReqManager.exchangeAutoMonitorLotCarrier(transactionLog, monitorInfo, targetCarrierRrn);

                    if(AutoMonitorUtils.checkJobStatusIsFixed(monitorInfo.getJobStatus())) {
                        lotAutoMonitorReqManager.deleteMonitorCarrierMappingByLot(monitorInfo.getLotRrn());
                    }
                }
            }
            buildCarrierMappingInfo(transactionLog, sourceCarrierRrn);
            buildCarrierMappingInfo(transactionLog, targetCarrierRrn);

            SorterBean temp = new SorterBean(sb.getMainJobRrn());
            temp.setSubJobRrn(sb.getSubJobRrn());
            sorterDAO.updateSorterBean(temp);//回填子批ID

            transactionLogManager.markTransactionLog(transactionLog);
        }

        private void runCardSplit(SortJobBean sortJobBean) {
            //回填子批的LotID
            SorterBean sb = SorterEnum.getListFirst(sortJobBean.getSorterBeans());
            long lotRrn = SorterEnum.getListFirst(sb.getSorterDetailBeanList()).getLotRrn();
            //先把子批split出去
            Lot childLot = splitRcLotForSort(sortJobBean, sb, "");

            LotRunCardStore runCardStore = runCardQueryManager.getSplitRunCardLotStore(lotRrn);
            List<LotRunCardSplit> splitSet = runCardQueryManager.getLotRunCardSplit(runCardStore.getRuncardRrn());
            List<Map<String, Object>> splitedLots = runCardQueryManager.getSplitedRunCardLots(runCardStore.getRuncardRrn());

            List<LotRunCardSplit> splitsList = new ArrayList<>();
            for (LotRunCardSplit lotRunCardSplit : splitSet) {
                boolean notSplit = true;
                for (Map<String, Object> splitedLot : splitedLots) {
                    int splitSeq = MapUtils.getIntValue(splitedLot, "splitSeq");
                    if (splitSeq != 0 && splitSeq == lotRunCardSplit.getSplitSeq()) {
                        notSplit = false;
                        break;
                    }
                }
                if (notSplit) {
                    splitsList.add(lotRunCardSplit);
                }
            }
            if (splitsList.size() == 1) {
                sb.setJsonAttributeData2(splitsList.get(0).getSplitSeq().toString());
                splitRcLotForSort(sortJobBean, sb, "1");
            }

            SorterBean temp = new SorterBean(sb.getMainJobRrn());
            temp.setSubJobRrn(sb.getSubJobRrn());
            temp.setJsonAttributeData4(childLot.getLotId());
            sorterDAO.updateSorterBean(temp);//回填子批ID

        }

        private void runCardMerge(SortJobBean sortJobBean) {
            List<SorterBean> sorterBeans = sortJobBean.getSorterBeans();
            SorterBean firstSorterBean = SorterEnum.getListFirst(sorterBeans);
            String userId = firstSorterBean.getCreateUser();

            Long sourceCarrierRrn = sortJobBean.getSourceCarrierRrn();
            Long targetCarrierRrn = sortJobBean.getTargetCarrierRrn();

            Lot parentLot = lotQueryManager.getLotByCarrierRrn(targetCarrierRrn);//需要放母批
            Lot childLot = lotQueryManager.getLotByCarrierRrn(sourceCarrierRrn); //必须放子批

            if (LotStatus.isRunCardHold(parentLot.getLotStatus())) {
                parentLot = lotQueryManager.getLot(RunCardUtils.buildMainRcLotId(parentLot.getLotId()),
                                                   LocalContext.getFacilityRrn());
            }

            List<Unit> childUnitList = unitQueryManager.getUnitList(childLot.getLotRrn());
            List<Unit> parentUnitList = unitQueryManager.getUnitList(parentLot.getLotRrn());

            List<Unit> newUnitList = new ArrayList<>();
            newUnitList.addAll(parentUnitList);
            newUnitList.addAll(childUnitList);

            RunCardMergeInfoDTO mergeInfoDTO = new RunCardMergeInfoDTO();
            mergeInfoDTO.setParentLot(parentLot);
            mergeInfoDTO.setChildLot(childLot);
            mergeInfoDTO.setParentUnitList(parentUnitList);
            mergeInfoDTO.setChildUnitList(childUnitList);
            mergeInfoDTO.setNewUnitList(newUnitList);

            mergeInfoDTO.setBySort(true);
            mergeInfoDTO.setSorterEqptRrn(sortJobBean.getEquipmentRrn() > 0 ? sortJobBean.getEquipmentRrn() : null);
            mergeInfoDTO.setSorterEqptId(sortJobBean.getEquipmentId());

            lotRunCardManager.mergeSplitRunCardLot(mergeInfoDTO, userId);
            childLot = lotQueryManager.getLot(childLot.getLotRrn());
            lotManager.calcStepSpeedTime(childLot.getLotStatus(), childLot.getBeforeStatus(), childLot.getLotRrn(), false);
            parentLot = lotQueryManager.getLot(parentLot.getLotRrn());
            lotManager.calcStepSpeedTime(parentLot.getLotStatus(), parentLot.getBeforeStatus(), parentLot.getLotRrn(),
                                         false);
        }

        private Lot splitRcLotForSort(SortJobBean sortJobBean, SorterBean sb, String isMainLot) {
            Lot lot = lotQueryManager.getLotByCarrierRrn(sb.getSourceCarrierRrn()); //mainLotId
            String mainLotId = lot.getLotId();
            Long facilityRrn = lot.getFacilityRrn();
            if (StringUtils.equals(lot.getLotStatus(), LotStatus.RUNCARD_HOLD)) {
                lot = lotQueryManager.getLot(lot.getLotId() + RunCardConstants.MAINLOT_ID_IN_MES,
                                             facilityRrn);//RunCard LotId
            }

            LotRunCardStore runCardStore = runCardQueryManager.getSplitRunCardLotStore(lot.getLotRrn());

            //组装RunCardSplitLot必要的参数
            int splitSeq = NumberUtils.toInt(sb.getJsonAttributeData2(), -1);

            String carrierId = sb.getTargetCarrierId();
            String lotId = lot.getLotId();
            String runcardId = runCardStore.getRuncardId();
            long runCardRrn = namedObjectManager.getNamedObjectRrn(runcardId, facilityRrn, ObjectList.ECN_KEY);
            long carrierRrn = sb.getTargetCarrierRrn();

            List<LotRunCardSplit> splitSet = runCardQueryManager.getLotRunCardSplit(runCardRrn);

            LotRunCardSplit splitInfo = splitSet.stream().filter(set -> set.getSplitSeq() == splitSeq).findFirst().get();

            //校验Runcard的子批是否已经被分出去了
            List<Map<String, Object>> lotMaps = runCardQueryManager.getRunCardLotsByRuncardRrn(runCardRrn);
            for (Map<String, Object> lotMap : lotMaps) {
                Assert.isFalse(MapUtils.getLongValue(lotMap, "splitSeq") == splitSeq,
                               Errors.create().key(MessageIdList.LOT_HAS_SPLIT).build());
            }

            int childLotCount = runCardQueryManager.getRCChildLotCount(mainLotId, StringUtils.substring(runcardId, 0, 1));
            String childLotIdMax = SorterEnum.HardCode.S +
                    (childLotCount + 1 < 10 ? SorterEnum.HardCode.ZERO_STR + (childLotCount + 1) : childLotCount + 1);

            List<RunCardSplitInfoDTO> lotList = new ArrayList<>();
            if (splitInfo != null) {
                lotList = lotRunCardManager.buildSplitInfo(splitInfo, lotList, lot, splitSeq, runCardRrn, carrierRrn,
                                                           childLotCount, lotId, mainLotId, isMainLot, runcardId,
                                                           facilityRrn, carrierId);
                SorterEnum.getListFirst(lotList).setBySort(true);
                SorterEnum.getListFirst(lotList).setSorterEqptId(sortJobBean.getEquipmentId());
                SorterEnum.getListFirst(lotList).setSorterEqptRrn(sortJobBean.getEquipmentRrn());
            }

            lotRunCardManager.splitRunCardLots(lotList, LocalContext.getUserId());
            if (CollectionUtils.isNotEmpty(lotList)) {
                for (RunCardSplitInfoDTO dto : lotList) {
                    if (dto.getChildLot() != null) {
                        Long childLotRrn = dto.getChildLot().getLotRrn();
                        if (childLotRrn > 0) {
                            Lot currentLot = lotQueryManager.getLot(childLotRrn);
                            lotManager.calcStepSpeedTime(currentLot.getLotStatus(), currentLot.getBeforeStatus(),
                                                         currentLot.getLotRrn(), false);
                        }
                    }
                }
            }
            return lotQueryManager.getLot(mainLotId + "." + childLotIdMax, facilityRrn);
        }

        private void splitTo3Port(SortJobBean sortJobBean) {
            List<SorterBean> jobList = sortJobBean.getSorterBeans();
            Assert.isFalse(CollectionUtils.isEmpty(jobList) || jobList.size() != 2,
                           Errors.create().key(MessageIdList.UNIT_DATA_ERROR).build());
            SorterBean t1 = SorterEnum.getListFirst(jobList);
            SorterBean t2 = jobList.get(1);
            // 1.将details中的晶圆分批到P1晶舟
            // childLot = splitLotForSort(sortJob, details, user, transactionLog);
            SortJobBean job1 = new SortJobBean();
            job1.setSorterBeans(t1);
            splitLot(job1, null);           //子批通过Split动作拆分出去
            // 2.将其余晶圆交换到P2晶舟
            SortJobBean job2 = new SortJobBean();
            job2.setSorterBeans(t2);
            exchangeCarrier(job2, SorterEnum.Constant.EXCHANGE);    //剩余晶圆通过Exchange交换出去
        }

        private void exchangeCarrier(SortJobBean sortJobBean, String type) {
            long facilityRrn = LocalContext.getFacilityRrn();
            List<SorterBean> sorterBeans = sortJobBean.getSorterBeans();
            SorterBean sorterBean = SorterEnum.getListFirst(sorterBeans);
            List<SorterDetailBean> sorterDetailBeans = sorterBean.getSorterDetailBeanList();

            String lotId = SorterEnum.getListFirst(sorterDetailBeans).getLotId();
            Lot lot = lotQueryManager.getLot(lotId, facilityRrn);
            lot.setLotComments((SorterEnum.Constant.EXCHANGE.equalsIgnoreCase(type) ? "普通" : "特殊") + "交换晶舟，初始晶舟为： " +
                                       sorterBean.getSourceCarrierId() + "，SortJob Finish。");
            lot.setTransPerformedby(sorterBean.getCreateUser());
            lot.setTransId(type);
            lot.setSorterFlag(true);
            lot.setLocation(sortJobBean.getEquipmentId());
            lot.setEqptRrn(sortJobBean.getEquipmentRrn());

            Carrier toCarrier = carrierManager.getCarrier(sorterBean.getTargetCarrierRrn());
            //Carrier 的一些校验
            Assert.isFalse(!StringUtils.equalsIgnoreCase(toCarrier.getCarrierStatus(), PcdStatus.FREE_KEY) &&
                                   !StringUtils.equalsIgnoreCase(toCarrier.getCarrierStatus(), PcdStatus.ASSEMBLY_KEY),
                           Errors.create().key(MessageIdList.CARRIER_STATUS_INVALID).args(toCarrier.getInstanceId()).build());

            List<Map> carrierMappings = buildCarrierMapping(sorterDetailBeans);
            carrierManager.exchangeCarrierSpecial(lot, toCarrier.getInstanceRrn(), carrierMappings, toCarrier, facilityRrn,
                                                  sorterBean.getCreateUser());

        }

        private void splitLot(SortJobBean sortJobBean, String type) {
            //目前仅支持 永久分批
            List<Map> childLots = new ArrayList<>();
            List<SorterBean> sorterBeans = sortJobBean.getSorterBeans();
            SorterBean firstSorterBean = SorterEnum.getListFirst(sorterBeans);
            String userId = firstSorterBean.getCreateUser();
            List<SorterDetailBean> sorterDetailBeans = firstSorterBean.getSorterDetailBeanList();
            SorterDetailBean firstSorterDetailBean = SorterEnum.getListFirst(sorterDetailBeans);

            long baseLotRrn = firstSorterDetailBean.getLotRrn();
            Lot lot = lotQueryManager.getLot(baseLotRrn);

            Carrier targetCarrier = carrierManager.getCarrier(firstSorterBean.getTargetCarrierRrn());
            // carrier的一些校验
            Assert.isFalse(!StringUtils.equalsIgnoreCase(targetCarrier.getCarrierStatus(), PcdStatus.FREE_KEY) &&
                                   !StringUtils.equalsIgnoreCase(targetCarrier.getCarrierStatus(), PcdStatus.ASSEMBLY_KEY),
                           Errors.create().key(MessageIdList.CARRIER_STATUS_INVALID).args(targetCarrier.getInstanceId())
                                 .build());

            //创建子批ID
            String childLotId = lotManager.buildChildLotId(lot.getLotId(), new ArrayList());
            //解析子批中的晶圆
            List<Map> childLotUnits = buildCarrierMapping(sorterDetailBeans);
            Map childLot = buildChildLot(lot, childLotId, targetCarrier, childLotUnits);
            childLots.add(childLot);
            List<Unit> parentLotUnits = unitQueryManager.getUnitList(lot.getLotRrn());
            //计算排除，先收集需要分批出去的晶圆，然后用母批现有晶圆去减
            List unitsOfParentLot = calculateRemainUnitList(parentLotUnits, childLots);

            Assert.isFalse(parentLotUnits.size() == unitsOfParentLot.size(),
                           Errors.create().key(MessageIdList.CHILD_LOT_UNIT_DATA_ERROR).build());
            HashMap valueMap = new HashMap();
            lot.setSorterFlag(true);
            lot.setLocation(sortJobBean.getEquipmentId());
            lot.setEqptRrn(sortJobBean.getEquipmentRrn());

            valueMap.put("lot", lot);
            valueMap.put("childLots", childLots);
            valueMap.put("splitType", SorterEnum.HardCode.PERMANENT);
            // valueMap.put("subSplitType", copyForm.getSubSplitType());
            valueMap.put("unitOfParent", unitsOfParentLot);
            String comment = "Sort Job Split ParentLot:" + lot.getLotId() + " ChildLot:" + childLotId;
            valueMap.put("comment", comment);
            valueMap.put("facilityRrn", LocalContext.getFacilityRrn());
            valueMap.put("user", userId);

            TransReason transReason = new TransReason();
            transReason.setReasonCode(SorterEnum.Constant.BLANK);
            transReason.setReason(comment);
            transReason.setResponsibility(userId);
            transReason.setTransQty1((double) childLotUnits.size());
            HashMap map = null;
            // 先获取母批中的所有晶圆数量，减去要分批出去的晶圆，就是 remainQty
            if (CollectionUtils.isEmpty(unitsOfParentLot)) {//重新计算 remainQty 目标会涉及到释放晶舟
                map = new HashMap();
                map.put("lotRrn", lot.getLotRrn());
                map.put("transId", SorterEnum.HardCode.TERMINATE);
                map.put("user", userId);

                TransReason transR = new TransReason();
                transR.setReasonCategory(SorterEnum.HardCode.TERM);
                transR.setReason(SorterEnum.HardCode.NO_QTY);
                transR.setReasonCode(SorterEnum.Constant.SPLIT);
                transR.setResponsibility(userId);
                transR.setTransQty1(lot.getQty1());
                transR.setTransQty2(lot.getQty2());

                map.put("transReason", transR);
                map.put("comments", "");
            }
            valueMap.put("terminateMap", map);
            valueMap.put("transReason", transReason);
            valueMap.put(SystemConstant.Str.ACTION_FLAG, SystemConstant.Str.CAL_STEP_TIME);
            if (StringUtils.equals(sortJobBean.getJobType().toUpperCase(), SorterEnum.Constant.PILOT_SPLIT)) {
                valueMap.put("subSplitType", "pilot");
            }
            lotManager.splitLotWithTerminate(valueMap);
            if (CollectionUtils.isNotEmpty(childLots)) {
                //子批次开始时间
                for (Map childLotMap : childLots) {
                    String childLotIdTemp = MapUtils.getString(childLotMap, "childLotId");
                    Lot childLotTemp = lotQueryManager.getLot(childLotIdTemp, LocalContext.getFacilityRrn());
                    if (childLotTemp == null) {
                        continue;
                    }
                    //#39709 xsl split之后子批重置计时	计时重置，根据当前状态开始计时
                    //劈出来的新批次
                    lotManager.calcStepSpeedTime(childLotTemp.getLotStatus(), childLotTemp.getBeforeStatus(),
                                                 childLotTemp.getLotRrn(), true);
                }
            }
            SorterBean sb = new SorterBean(firstSorterBean.getMainJobRrn());
            sb.setSubJobRrn(firstSorterBean.getSubJobRrn());
            sb.setJsonAttributeData4(childLotId);
            sorterDAO.updateSorterBean(sb);//回填子批ID
        }

        private void merge(SortJobBean sortJobBean) {
            List<SorterBean> sorterBeans = sortJobBean.getSorterBeans();
            SorterBean firstSorterBean = SorterEnum.getListFirst(sorterBeans);
            String userId = firstSorterBean.getCreateUser();

            Long sourceCarrierRrn = sortJobBean.getSourceCarrierRrn();
            Long targetCarrierRrn = sortJobBean.getTargetCarrierRrn();

            Lot parentLot = lotQueryManager.getLotByCarrierRrn(targetCarrierRrn);
            parentLot.setSorterFlag(true);
            parentLot.setLocation(sortJobBean.getEquipmentId());
            parentLot.setEqptRrn(sortJobBean.getEquipmentRrn());
            Lot childLot = lotQueryManager.getLotByCarrierRrn(sourceCarrierRrn);

            // build子批信息
            List<Map<String, Object>> childLots = new ArrayList<>();// 被合子批
            Map<String, Object> childLotMap = new HashMap<>();
            childLotMap.put("childLotId", childLot.getLotId());
            childLotMap.put("carrierId", childLot.getCarrierId());
            childLotMap.put("qty1", childLot.getQty1());
            childLotMap.put("qty2", childLot.getQty2());
            childLotMap.put("productId", childLot.getProductId());
            childLotMap.put("processId", childLot.getProcessId());
            childLotMap.put("operationId", childLot.getOperationId());

            List<Map> childUnits = unitQueryManager.getUnitListByCarrier(sourceCarrierRrn);
            childLotMap.put("units", childUnits);
            childLots.add(childLotMap);

            Assert.isFalse(firstSorterBean.getSorterDetailBeanList().size() != childUnits.size(),
                           Errors.create().key(MessageIdList.CHILD_LOT_UNIT_ERROR).args(childLot.getLotId()).build());

            //母批现有的晶圆列表
            List<Map> unitOfParent = unitQueryManager.getUnitListByLot(parentLot.getLotRrn());
            //需要母批最新的晶圆列表，母批现有晶圆+需要合批的子批的晶圆
            List newUnits = buildMergeUnit(unitOfParent, sortJobBean.getSorterBeans());
            //
            Map<String, Object> valueMap = new HashMap<>();
            valueMap.put("lot", parentLot);// 目标批次 Lot对象, lotRrn即可
            valueMap.put("childLots", childLots);//childLotId、qty1、qty2、units
            valueMap.put("unitOfParent", unitOfParent);//母批当前在晶舟中的所有晶圆
            String comment = "Sort Job Merge ParentLot:" + parentLot.getLotId() + " ChildLot:" + childLot.getLotId();
            valueMap.put("comment", comment);
            valueMap.put("newUnits", newUnits);

            TransReason transReason = new TransReason();
            transReason.setReasonCode("");
            transReason.setResponsibility(userId);
            transReason.setReason(comment);
            valueMap.put("transReason", transReason);

            checkLotPriority(valueMap, parentLot, childLots);
            //创建需要上锁的 rrn arry
            List<String> needLockLotRrns = new ArrayList<>();
            needLockLotRrns.add(StringUtils.toString(childLot.getLotRrn()));
            valueMap.put(SystemConstant.Str.ACTION_FLAG, SystemConstant.Str.NORMAL_MERGE);
            valueMap.put(SystemConstant.Str.LOT_RRN_LIST, needLockLotRrns);
            lotManager.mergeLot(valueMap);
        }

        private void flipSideCarrier(SortJobBean sortJobBean) {
            List<SorterBean> sorterBeans = sortJobBean.getSorterBeans();
            SorterBean firstSorterBean = SorterEnum.getListFirst(sorterBeans);
            String flipStr = firstSorterBean.getJsonAttributeData3();
            String[] flipArr = StringUtils.split(flipStr, "|");
            Assert.isFalse(StringUtils.isBlank(flipStr) || flipArr.length != 2,
                           Errors.create().key(MessageIdList.FLIP_DATA_ERROR).content("Flip data Error!").build());

            Lot sourceLot = lotQueryManager.getLotByCarrierRrn(firstSorterBean.getSourceCarrierRrn());
            if (LotStatus.isRunCardHold(sourceLot.getLotStatus())) {
                sourceLot = lotQueryManager.getLot(RunCardUtils.buildMainRcLotId(sourceLot.getLotId()),
                                                   sourceLot.getFacilityRrn());
            }
            Boolean state = StringUtils.equalsIgnoreCase(sourceLot.getFlipType(), flipArr[0]);
            Assert.state(state, Errors.create().key(MessageIdList.FLIP_DATA_ERROR).content("Flip data Error!").build());
            //记录机台号
            sourceLot.setEqptRrn(sortJobBean.getEquipmentRrn());
            sourceLot.setLocation(sourceLot.getEqptID());
            lotManager.flipSideByLot(sourceLot, flipArr[1], true);
        }

        private List<Map> dealSelectedUnits(List<Map> currentUnits, List<Map> selectedUnitList, String positionKey,
                                            boolean isRemove) {
            List<Map> unitMapList;
            Map[] unitArr = new Map[26];
            for (Map m : currentUnits) {
                int position = MapUtils.getIntValue(m, "position");
                if (checkArrIndexRange(position)) {
                    unitArr[position] = m;
                }
            }
            for (Map sm : selectedUnitList) {
                int position = MapUtils.getIntValue(sm, positionKey);
                if (checkArrIndexRange(position)) {
                    if (isRemove) {
                        unitArr[position] = null;
                    } else {
                        unitArr[position] = sm;
                    }
                }
            }
            unitMapList = Arrays.stream(unitArr).filter(Objects::nonNull).collect(Collectors.toList());
            return unitMapList;
        }

        /**
         * 计算母批在分批后的剩余晶圆
         *
         * @param parentLotUnits
         * @param childLots
         * @return
         */
        private List calculateRemainUnitList(List<Unit> parentLotUnits, List<Map> childLots) {
            if (CollectionUtils.isEmpty(childLots)) {
                return parentLotUnits;
            }
            List<Map> unitMapList;
            Map[] unitMapArr = new Map[25];
            for (Unit u : parentLotUnits) {
                Map<String, Object> unitMap = new HashMap<>(8);
                if (checkArrIndexRange(u.getPositionInCarrier())) {
                    unitMap.put("position", u.getPositionInCarrier());
                    unitMap.put("unitId", u.getUnitId());
                    unitMap.put("unitRrn", u.getUnitRrn());
                    unitMap.put("lotId", u.getLotId());
                    unitMapArr[u.getPositionInCarrier() - 1] = unitMap;
                }
            }
            for (Map map : childLots) {

                List<Map> units = (List<Map>) MapUtils.getObject(map, "unitList");
                List<Map> unitList = new ArrayList<>();//获得移除空位置后的晶圆列表
                if (CollectionUtils.isNotEmpty(units)) {
                    for (Map unit : units) {
                        //"unitId":"A0P001#22","unitRrn":"6172996","lotId":"A0P001"
                        String lotId = MapUtils.getString(unit, "lotId");
                        String unitId = MapUtils.getString(unit, "unitId");
                        Long unitRrn = MapUtils.getLong(unit, "unitRrn");
                        if (StringUtils.isNotEmpty(lotId) && StringUtils.isNotEmpty(unitId) && unitRrn != null) {
                            int postion = MapUtils.getIntValue(unit, "position");
                            if (checkArrIndexRange(postion)) {
                                unitMapArr[postion - 1] = null;
                                unitList.add(unit);
                            }
                        }
                    }
                    map.put("units", unitList);
                }
            }

            unitMapList = Arrays.stream(unitMapArr).filter(Objects::nonNull).collect(Collectors.toList());
            return unitMapList;
        }

        private List<Map> buildCarrierMapping(List<SorterDetailBean> sorterDetailBeans) {
            return sorterDetailBeans.stream().map(m -> {
                Map map = new HashMap();
                map.put("lotRrn", m.getLotRrn());
                map.put("lotId", m.getLotId());
                map.put("unitRrn", m.getUnitRrn());
                map.put("unitId", m.getUnitId());
                map.put("oldPosition", m.getSourcePosition());
                map.put("position", m.getTargetPosition());
                return map;
            }).collect(Collectors.toList());
        }

        private List buildMergeUnit(List<Map> unitOfParent, List<SorterBean> sorterBeans) {
            Map[] unitMaps = new Map[25];
            for (Map m : unitOfParent) {
                int position = MapUtils.getIntValue(m, "position");
                if (position > 0)
                    unitMaps[position - 1] = m;
            }
            for (SorterBean sb : sorterBeans) {
                for (SorterDetailBean sdb : sb.getSorterDetailBeanList()) {
                    if (sdb.getTargetPosition() > 0) {
                        Map m = new HashMap();
                        m.put("position", sdb.getTargetPosition());
                        m.put("unitId", sdb.getUnitId());
                        m.put("unitRrn", sdb.getUnitRrn());
                        m.put("lotId", sdb.getLotId());
                        m.put("lotRrn", sdb.getLotRrn());
                        unitMaps[sdb.getTargetPosition() - 1] = m;
                    }
                }
            }
            return Arrays.stream(unitMaps).filter(Objects::nonNull).collect(Collectors.toList());
        }

        private boolean checkArrIndexRange(int range) {
            return range >= 1 && range <= 25;
        }

        /**
         * @param parentLot
         * @param childLotId
         * @param targetCarrier
         * @param units
         * @return Map<String, Object>
         * @Description 分批时构建子批信息
         * @author Aiden
         */
        private Map<String, Object> buildChildLot(Lot parentLot, String childLotId, Carrier targetCarrier,
                                                  List<Map> units) {
            Map<String, Object> childLot = new HashMap<>();
            childLot.put("childLotId", childLotId);
            childLot.put("carrierId", targetCarrier.getInstanceId());
            childLot.put("carrierRrn", targetCarrier.getInstanceRrn());
            childLot.put("splitQty", (double) units.size());
            childLot.put("intSplitQty", units.size() + StringUtils.EMPTY);
            childLot.put("technologyRrn", parentLot.getProcessRrn());
            childLot.put("wflStepPath", null);
            childLot.put("routeId", parentLot.getRouteId());
            childLot.put("operationId", parentLot.getOperationId());
            childLot.put("operationRrn", parentLot.getOperationRrn());
            childLot.put("operationDesc", parentLot.getOperationDesc());
            childLot.put("unitList", units);
            childLot.put("units", units);
            childLot.put("changeProcessFlag", SorterEnum.HardCode.FALSE);
            childLot.put("changeAcutl", SorterEnum.HardCode.MINUS_ONE);
            return childLot;
        }

        private void checkLotPriority(Map<String, Object> valueMap, Lot lot, List<Map<String, Object>> childLots) {
            // Check the priority size of sub-lot and lot
            Integer tempHotFlag = NumberUtils.toInt(lot.getHotFlag(), Integer.MAX_VALUE);
            Integer tempProirity = Optional.ofNullable(lot.getPriority()).orElse(Integer.MAX_VALUE);
            boolean changeFlag = false;
            for (Map<String, Object> subLotMap : childLots) {
                Lot subLot = lotQueryManager.getLot(MapUtils.getString(subLotMap, "childLotId"), lot.getFacilityRrn());
                Integer hotFlag = NumberUtils.toInt(subLot.getHotFlag(), Integer.MAX_VALUE);
                Integer priority = Optional.ofNullable(subLot.getPriority()).orElse(Integer.MAX_VALUE);
                if (hotFlag < tempHotFlag || (hotFlag.equals(tempHotFlag) && priority < tempProirity)) {
                    changeFlag = true;
                    tempHotFlag = hotFlag;
                    tempProirity = priority;
                }
            }
            if (changeFlag) {
                valueMap.put("hotFlag", tempHotFlag);
                valueMap.put("priority", tempProirity);
            }
        }

        private void buildCarrierMappingInfo(TransactionLog transactionLog, Long carrierRrn) {
            List<Unit> carrierUnitList = new ArrayList<>();
            lotInqManager.getLotListByCarrierRrn(carrierRrn).stream().forEach(lot -> {
                carrierUnitList.addAll(unitQueryManager.getUnitList(lot.getLotRrn()));
            });
            carrierManager.updateCarrierMapping(transactionLog, carrierRrn, carrierUnitList);
        }

        private void buildReadT7CodeSortInfo(SorterBean sorterBean, Lot lot) {
            // 构建units
            List<Map> sourceUnits = unitQueryManager.getUnitListByCarrier(lot.getCarrierRrn());
            List<SorterDetailBean> sourceUnitList = sourceUnits.stream().map(unitMap -> {
                String unitId = MapUtils.getString(unitMap, "unitId");
                long unitRrn = MapUtils.getLong(unitMap, "unitRrn", 0L);
                String lotId = MapUtils.getString(unitMap, "lotId");
                long lotRrn = MapUtils.getLong(unitMap, "lotRrn", 0L);
                int position = MapUtils.getIntValue(unitMap, "position", 0);
                String customerT7Code = StringUtils.EMPTY;
                long operationRrn = lot.getOperationRrn();
                return new SorterDetailBean(lotRrn, lotId, unitRrn, unitId, position, position);
            }).collect(Collectors.toList());

            sorterBean.setSorterDetailBeanList(sourceUnitList);
            sorterBean.setExchangeTotalQty(sourceUnits.size());
        }

        @Override
        public void endReadT7Code(List<Unit> units) {
            TransactionLog transactionLog = transactionLogManager.startTransactionLog(LocalContext.getUserId(),
                                                                                      TransactionNames.CUSTOMER_T7CODE);
            try {
                //校验数据正确性
                Lot lot = new Lot();
                StringBuffer unitT7Code = new StringBuffer();
                if(units.get(0).getLotRrn()!=null&&units.get(0).getLotRrn()>0){
                    lot = lotQueryManager.getLot(units.get(0).getLotRrn());
                }
                for (Unit unit : units){
                    if(unit.getUnitRrn()<=0){
                      Unit validUnit =  unitQueryManager.getUnit(LocalContext.getFacilityRrn(),unit.getUnitId());
                        Assert.isTrue(validUnit!=null,Errors.create().key(MessageIdList.UNIT_ID_MISSING).args(unit.getUnitId()).build());
                        Assert.isTrue(validUnit.getUnitRrn()>0,Errors.create().key(MessageIdList.UNIT_ID_MISSING).args(unit.getUnitId()).build());
                      unit.setUnitRrn(validUnit.getUnitRrn());
                    }
                    unitT7Code.append(unit.getUnitId()+"->"+(StringUtils.isEmpty(unit.getCustomerT7Code())?StringUtils.EMPTY: unit.getCustomerT7Code())+"; ");
                    //更新unit表中customer t7code字段
                    unitManager.updateCustomerT7Code(unit);
                }
                //插入数据到UNIT_T7CODE_MAPPING_H表中
                unitManager.insertUnitT7CodeMappingH(units,transactionLog);
                lot.setTransId(TransactionNames.CUSTOMER_T7CODE);
                lot.setLotComments(unitT7Code.toString());
                //插入数据到lot trans history中
                lotManager.insertLotTransH(transactionLog.getTransRrn(),1L,lot);
                transactionLogManager.markTransactionLog(transactionLog);
            } catch (Exception e) {
                logger.error(e);
                throw new SystemIllegalArgumentException(Errors.create().content(e.getMessage()).build());
            }
        }

        @Override
        public void checkCustomerT7Code(List<Unit> units) {
            //校验units不能为空
            Assert.isFalse(CollectionUtils.isEmpty(units),Errors.create().key(MessageIdList.CUSTOMER_T7CODE_CAN_NOT_EMPTY).build());

            Lot lot = lotQueryManager.getLot(units.get(0).getLotRrn());
            Assert.state(lot != null, Errors.create().key(MessageIdList.LOT_MISSING_ID).content("No such lotId!").build());
            Assert.state(lot.getLotRrn() > 0, Errors.create().key(MessageIdList.LOT_MISSING_ID).content("No such lotId!").build());
            //卡控每片wafer上都要有t7code
            List<Unit> unitList = unitQueryManager.getUnitList(lot.getLotRrn());
            Assert.isTrue(NumberUtils.compare(unitList.size(),units.size())==0,Errors.create().key(MessageIdList.UNIT_AND_CUSTOMERT7CODE_COUNT_NOT_SAME).build());

            units.stream().forEach(unit -> {
                //校验unit id是否存在
                Unit validUnit =  unitQueryManager.getUnit(LocalContext.getFacilityRrn(),unit.getUnitId());
                Assert.isTrue(validUnit!=null, Errors.create().key(MessageIdList.UNIT_ID_MISSING).args(unit.getUnitId()).build());
                Assert.isTrue(validUnit.getUnitRrn()>0, Errors.create().key(MessageIdList.UNIT_ID_MISSING).args(unit.getUnitId()).build());
                //晶圆位置不能为空
                Assert.isFalse(unit.getPositionInCarrier()==null,Errors.create().key(MessageIdList.SLOT_CAN_NOT_EMPTY).build());
                //校验卡槽数是否一致
                List<Unit> compareUnitList = unitList.stream().filter(u ->
                    StringUtils.equals(u.getUnitId(),unit.getUnitId())&&NumberUtils.compare(u.getPositionInCarrier(),unit.getPositionInCarrier())==0
                ).collect(Collectors.toList());
                Assert.isTrue(CollectionUtils.isNotEmpty(compareUnitList),Errors.create().key(MessageIdList.UNIT_ID_AND_SLOT_NOT_MATCH).args(new Object[]{unit.getUnitId(),unit.getPositionInCarrier()}).build());
                //校验是否有空的customer t7code
                Assert.isFalse(StringUtils.isEmpty(unit.getCustomerT7Code()), Errors.create().key(MessageIdList.UNIT_T7CODE_CAN_NOT_EMPTY).args(unit.getUnitId()).build());
                //customer t7code只能绑定一个unit
                List<Unit> currentT7codeUnits = unitQueryManager.getUnitListByCustomerT7Code(unit.getCustomerT7Code());
                Assert.isFalse(CollectionUtils.isNotEmpty(currentT7codeUnits),Errors.create().key(MessageIdList.CUSTOMER_T7CODE_IS_EXIST).args(unit.getCustomerT7Code()).build());
            });
            List<Unit> beforeUnits = new ArrayList<>();
            //校验同一个卡槽中customer t7code不能相同
            int beforeCount = units.size();
            beforeUnits = units;
            units = units.stream().collect(collectingAndThen(toCollection(() -> new TreeSet<>(Comparator.comparing(Unit::getCustomerT7Code))), ArrayList::new));
            List<Unit> finalUnits = units;//去重后的数据
            beforeUnits.stream().forEach(beforeUnit -> {
                    Assert.isTrue(finalUnits.contains(beforeUnit),Errors.create().key(MessageIdList.CUSTOMER_T7CODE_IS_EXIST).args(beforeUnit.getCustomerT7Code()).build());
            });
        }

        @Override
        public void updateSorterJobForInLineExchange(SortJobBean sortJobBean, Lot lot) {
            Carrier carrier = checkTargetCarrier(sortJobBean.getTargetCarrierId(), lot);
            SorterBean endSortJobBean = new SorterBean();
            endSortJobBean.setMainJobRrn(sortJobBean.getMainJobRrn());
            endSortJobBean.setTargetCarrierId(sortJobBean.getTargetCarrierId());
            endSortJobBean.setTargetCarrierRrn(carrier.getInstanceRrn());
            sorterDAO.updateSorterBean(endSortJobBean);
        }

        @Override
        public String endSortJobForInlineExchange(SortJobBean sortJobBean, Lot lot) {
            updateSorterJobForInLineExchange(sortJobBean, lot);
            return sorterManager.endSortJob(sortJobBean);
        }

        @Override
        public Carrier checkTargetCarrier(String targetCarrierId, Lot lot) {
            Carrier carrier = carrierManager.getCarrier(LocalContext.getFacilityRrn(), targetCarrierId);

            Assert.state(carrier != null,
                           Errors.create().key(MessageIdList.CARRIER_CASSETTE_ID_NOT_EXIST).build());
            Assert.state(carrier.getInstanceRrn() > 0,
                         Errors.create().key(MessageIdList.CARRIER_CASSETTE_ID_NOT_EXIST).build());

            String targetCSTType = lotQueryManager.getToCarrierTypeForChange(lot);
            Assert.state(StringUtils.equalsIgnoreCase(targetCSTType, carrier.getObjectSubtype()),
                           Errors.create().key(MessageIdList.BOND_CARRIER_TYPE_MIS_MATCH).build());

            boolean isAssemblyNeed = StringUtils.equalsIgnoreCase(
                    referenceFileManager.getReferenceDetailExchange(ReferenceDetailNames.CARRIER_TYPE,
                                                                    carrier.getObjectSubtype(), null,
                                                                    ReferenceFileConst.DATA_2_VALUE),
                    SorterEnum.HardCode.ASSEMBLY_NEED_KEY);

            Assert.isFalse(isAssemblyNeed && !StringUtils.equals(carrier.getCarrierStatus(), PcdStatus.ASSEMBLY_KEY),
                           Errors.create().key(MessageIdList.CARRIER_NEED_ASSEMBLED).args(carrier.getInstanceId()).build());

            Assert.isTrue(isAssemblyNeed || StringUtils.equals(carrier.getCarrierStatus(), PcdStatus.FREE_KEY),
                          Errors.create().key(MessageIdList.CARRIER_STATUS_INVALID).args(carrier.getInstanceId()).build());
            //判断是否超过清洗时间
            boolean overCleanFlag = pcdManager.checkPcdOverClean(carrier.getInstanceRrn());
            Assert.isTrue(overCleanFlag, Errors.create().key(MessageIdList.CARRIER_OUT_OF_CLEAN_DATE)
                                               .content("Cassette is out of clean date!").build());

            return carrier;
        }

        @Override
        public void checkUnitMaterialID(List<Unit> units) {
            //校验units不能为空
            Assert.isFalse(CollectionUtils.isEmpty(units),
                           Errors.create().key(MessageIdList.MATERIAL_ID_NOT_EMPTY).build());

            Lot lot = lotInqManager.getLot(units.get(0).getLotRrn());
            Assert.state(lot != null, Errors.create().key(MessageIdList.LOT_MISSING_ID).content("No such lotId!").build());
            Assert.state(lot.getLotRrn() > 0,
                         Errors.create().key(MessageIdList.LOT_MISSING_ID).content("No such lotId!").build());
            //卡控每片wafer上都要有materialID
            List<Unit> unitList = unitQueryManager.getUnitList(lot.getLotRrn());
            Assert.isFalse(unitList.size() != units.size(),
                           Errors.create().key(MessageIdList.UNIT_MATERIALID_COUNT_NOT_SAME).build());

            units.stream().forEach(unit -> {
                //校验unit id是否存在
                Unit validUnit = unitQueryManager.getUnit(LocalContext.getFacilityRrn(), unit.getUnitId());
                Assert.isTrue(validUnit != null,
                              Errors.create().key(MessageIdList.UNIT_ID_MISSING).args(unit.getUnitId()).build());
                Assert.isTrue(validUnit.getUnitRrn() > 0,
                              Errors.create().key(MessageIdList.UNIT_ID_MISSING).args(unit.getUnitId()).build());

                //晶圆位置不能为空
                Assert.isFalse(unit.getPositionInCarrier() == null,
                               Errors.create().key(MessageIdList.SLOT_CAN_NOT_EMPTY).build());
                //校验卡槽数是否一致
                List<Unit> compareUnitList = unitList.stream()
                                                     .filter(u -> StringUtils.equals(u.getUnitId(), unit.getUnitId()) &&
                                                             NumberUtils.compare(u.getPositionInCarrier(),
                                                                                 unit.getPositionInCarrier()) == 0)
                                                     .collect(Collectors.toList());

                Assert.isTrue(CollectionUtils.isNotEmpty(compareUnitList),
                              Errors.create().key(MessageIdList.UNIT_ID_AND_SLOT_NOT_MATCH)
                                    .args(new Object[]{unit.getUnitId(), unit.getPositionInCarrier()}).build());
                //校验是否有空的MaterialID
                Assert.isFalse(StringUtils.isEmpty(unit.getMaterialId()),
                               Errors.create().key(MessageIdList.UNIT_MATERIALID_CAN_NOT_EMPTY).args(unit.getUnitId())
                                     .build());

                //material Id只能绑定一个unit
                List<Unit> materialIdUnits = unitQueryManager.getUnitListByMaterialId(unit.getMaterialId());
                Assert.isFalse(CollectionUtils.isNotEmpty(materialIdUnits),
                               Errors.create().key(MessageIdList.UNIT_MATERIALID_EXISTS).args(unit.getCustomerT7Code())
                                     .build());
            });

            List<Unit> beforeUnits = new ArrayList<>();
            //校验同一个卡槽中material Id不能相同
            beforeUnits = units;
            //去重后的数据
            units = units.stream().collect(
                    collectingAndThen(toCollection(() -> new TreeSet<>(Comparator.comparing(Unit::getMaterialId))),
                                      ArrayList::new));
            List<Unit> finalUnits = units;
            beforeUnits.stream().forEach(beforeUnit -> {
                Assert.isTrue(finalUnits.contains(beforeUnit),
                              Errors.create().key(MessageIdList.UNIT_MATERIALID_EXISTS).args(beforeUnit.getMaterialId())
                                    .build());
            });
        }

        @Override
        public void updateUnitMaterialID(List<Unit> units) {
            TransactionLog transactionLog = transactionLogManager
                    .startTransactionLog(LocalContext.getUserId(), TransactionNames.OFFLINE_READ_MATERIALID);
            try {
                //校验数据正确性
                Lot lot = new Lot();
                StringBuilder transComments = new StringBuilder();
                if (units.get(0).getLotRrn() != null && units.get(0).getLotRrn() > 0) {
                    lot = lotInqManager.getLot(units.get(0).getLotRrn());
                }
                for (Unit unit : units) {
                    if (unit.getUnitRrn() <= 0) {
                        Unit validUnit = unitQueryManager.getUnit(LocalContext.getFacilityRrn(), unit.getUnitId());
                        Assert.isTrue(validUnit != null,
                                      Errors.create().key(MessageIdList.UNIT_ID_MISSING).args(unit.getUnitId()).build());
                        Assert.isTrue(validUnit.getUnitRrn() > 0,
                                      Errors.create().key(MessageIdList.UNIT_ID_MISSING).args(unit.getUnitId()).build());
                        unit.setUnitRrn(validUnit.getUnitRrn());
                    }
                    transComments.append(unit.getUnitId() + "->" +
                                              (StringUtils.isEmpty(unit.getMaterialId()) ? StringUtils.EMPTY : unit
                                                      .getMaterialId()) + "; ");
                    //更新unit表中Material_ID字段
                    unitManager.updateUnitMaterialID(unit);
                }
                //插入数据到UNIT_MATERIAL_MAPPING_H表中
                unitManager.insertUnitMaterialMappingH(units, transactionLog);
                lot.setTransId(TransactionNames.OFFLINE_READ_MATERIALID);
                lot.setLotComments(transComments.toString());
                //插入数据到lot trans history中
                lotTransHistoryReqManager.createLotTransHistory(transactionLog, lot, transComments.toString());
                transactionLogManager.markTransactionLog(transactionLog);
            } catch (Exception e) {
                logger.error(e);
                throw new SystemIllegalArgumentException(Errors.create().content(e.getMessage()).build());
            }
        }

    }