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());
        }
    }

}