# CarrierManagerImpl.java

    package com.mycim.server.carrier.manager.impl;

    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.jdbc.Page;
    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.base.manager.EventManager;
    import com.mycim.server.base.manager.NamedObjectManager;
    import com.mycim.server.base.manager.TransactionLogManager;
    import com.mycim.server.carrier.dao.CarrierDAO;
    import com.mycim.server.carrier.dao.DoorDAO;
    import com.mycim.server.carrier.dao.PodDAO;
    import com.mycim.server.carrier.manager.CarrierManager;
    import com.mycim.server.carrier.manager.DoorManager;
    import com.mycim.server.carrier.manager.PcdManager;
    import com.mycim.server.carrier.manager.PodManager;
    import com.mycim.server.ctx.exec.manager.ProcessLocationContextManager;
    import com.mycim.server.ems.manager.EntityManager;
    import com.mycim.server.reticle.manager.LocationInqManager;
    import com.mycim.server.status.manager.StatusManager;
    import com.mycim.server.system.manager.ReferenceFileManager;
    import com.mycim.server.wip.manager.*;
    import com.mycim.utils.CheckRegexUtils;
    import com.mycim.utils.WipUtils;
    import com.mycim.valueobject.LotConstants;
    import com.mycim.valueobject.MessageIdList;
    import com.mycim.valueobject.ObjectList;
    import com.mycim.valueobject.SystemConstant;
    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.ems.PcdAssembly;
    import com.mycim.valueobject.ems.PcdClean;
    import com.mycim.valueobject.runcard.util.RunCardConstants;
    import com.mycim.valueobject.runcard.util.RunCardUtils;
    import com.mycim.valueobject.wip.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.util.*;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    import java.util.stream.Collectors;

    /**
     * @author yanbing.chen
     * @date 2019/10/10
     * @since 1.8
     **/
    @Service
    @Transactional
    public class CarrierManagerImpl implements CarrierManager {

        private static final String CSTTYPE_SEPERATER_KEY = "-";

        private static final Pattern PCDPATERN = Pattern.compile("(\\w{1,}\\-\\w{1,})");

        private static final Long SLOTMAXCOUNT = new Long(25);

        @Autowired
        CarrierDAO carrierDAO;

        @Autowired
        DoorDAO doorDAO;

        @Autowired
        PodDAO podDAO;

        @Autowired
        NamedObjectManager objectManager;

        @Autowired
        TransactionLogManager transactionLogManager;

        @Autowired
        EntityManager entityManager;

        @Autowired
        PcdManager pcdManager;

        @Autowired
        StatusManager statusManager;

        @Autowired
        EventManager eventManager;

        @Autowired
        PodManager podManager;

        @Autowired
        DoorManager doorManager;

        @Autowired
        LotManager lotManager;

        @Autowired
        LotQueryManager lotQueryManager;

        @Autowired
        ReferenceFileManager referenceFileManager;

        @Autowired
        ProcessLocationContextManager processLocationContextManager;

        @Autowired
        UnitQueryManager unitQueryManager;

        @Autowired
        RunCardQueryManager runCardQueryManager;

        @Autowired
        LotInqManager lotInqManager;

        @Autowired
        LocationInqManager locationInqManager;

        public void insertCarrierMappingH(Long transRrn, Long carrierMappingRrn) {
            carrierDAO.insertCarrierMappingH(transRrn, carrierMappingRrn);
        }

        /**
         * 交换晶舟
         *
         * @param transRrn        事务号
         * @param transSeq        事务seq
         * @param lot             晶舟上的批次
         * @param newCarrierRrn   新晶舟RRN
         * @param carrierMappings 批次中的晶圆
         */
        @Override
        public void exchangeCarrier(Long transRrn, Long transSeq, Lot lot, Long newCarrierRrn, List<Map> carrierMappings) {
            Long oldCarrierRrn = lot.getCarrierRrn();
            String carrierMappingRrn = IDGenerators.get(IDNames.SEQUENCE).generateId(SequenceNames.MODULE_SEQ_OBJECT_RRN);

            // String lotId = getInUseCarrierLotIdList(oldCarrierRrn).iterator().next();
            String lotId = lot.getLotId();
            Carrier carrier = new Carrier(newCarrierRrn);
            String carrierId = getCarrier(carrier).getInstanceId(); // 新晶舟ID

            // update New Carrier
            insertCarrierMapping(transRrn, newCarrierRrn, carrierMappings, Long.valueOf(carrierMappingRrn));
            carrier.setInstanceRrn(oldCarrierRrn);
            carrier = getCarrier(carrier); // 旧晶舟

            // 更新新晶舟数据
            updateCarrier(transRrn, carrier.getPollutionLevel(), newCarrierRrn, Long.valueOf(carrierMappingRrn), lotId, "",
                          PcdStatus.IN_USE_KEY);

            deleteCarrierMapping(transRrn, lot.getCarrierMapRrn());

            // 旧晶舟的处理
            PcdAssembly oldPcdAssembly = pcdManager.getPcdAssembly(oldCarrierRrn);
            String oldCarrierChangeStatus = (oldPcdAssembly == null) ? PcdStatus.FREE_KEY : PcdStatus.ASSEMBLY_KEY;

            if (StringUtils.equalsIgnoreCase(PcdStatus.WAIT_CLEAN_KEY, carrier.getCarrierStatus())) {
                carrierDAO.updateCarrierForInit(oldCarrierRrn, oldCarrierChangeStatus);
                carrierDAO.insertCarrierH(oldCarrierRrn, "Init cassette", carrierId,
                                          new TransactionLog(transRrn, LocalContext.getUserRrn()));
                PcdClean cleanInfo = pcdManager.getPcdCleanInfo(oldCarrierRrn);
                if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
                    pcdManager.insertPcdCleanH(transRrn, cleanInfo);
                }

                carrierDAO.updateCarrierForInit(oldCarrierRrn, PcdStatus.WAIT_CLEAN_KEY);
                carrierDAO.insertCarrierH(oldCarrierRrn, "Init cassette", carrierId,
                                          new TransactionLog(transRrn, LocalContext.getUserRrn()));
                if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
                    pcdManager.insertPcdCleanH(transRrn, cleanInfo);
                }
            } else {
                carrierDAO.updateCarrierForInit(oldCarrierRrn, oldCarrierChangeStatus);
                carrierDAO.insertCarrierH(oldCarrierRrn, "Init cassette", carrierId,
                                          new TransactionLog(transRrn, LocalContext.getUserRrn()));
                PcdClean cleanInfo = pcdManager.getPcdCleanInfo(oldCarrierRrn);
                if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
                    pcdManager.insertPcdCleanH(transRrn, cleanInfo);
                }
            }

            // update Lot And Units
            List<Long> inUseLotRrns = carrierDAO.getInUseCarrierLotRrnList(oldCarrierRrn);
            carrierDAO.updateLotForCarrierRrn(inUseLotRrns, newCarrierRrn);
            carrierDAO.updateUnitForCarrierRrn(oldCarrierRrn, newCarrierRrn);
            carrierDAO.updateUnitForCarrierMapping(carrierMappings);
            lot.setCarrierMapRrn(Long.valueOf(carrierMappingRrn));
            lot.setCarrierRrn(newCarrierRrn);
            createLotTransHistoryForExchanegCarrier(transRrn, transSeq, lot);
        }
        /**
         * 交换晶舟 For Src
         *
         * @param transRrn        事务号
         * @param transSeq        事务seq
         * @param lot             晶舟上的Src批次
         * @param targetCarrierRrn   新晶舟RRN
         * @param carrierMappings 批次中的晶圆
         */
        private void exchangeCarrier4Src(Long transRrn, Long transSeq, Lot lot, Long sourceCarrierRrn, Long sourceCarrierMapRrn, Long targetCarrierRrn, List<Map> carrierMappings, boolean needDealSourceCarrier) {
            String newCarrierMappingStr = IDGenerators.get(IDNames.SEQUENCE).generateId(SequenceNames.MODULE_SEQ_OBJECT_RRN);
            long newCarrierMappingRrn = Long.parseLong(newCarrierMappingStr);

            Carrier targetCarrier = getCarrier(new Carrier(targetCarrierRrn));  // 目标晶舟
            String targetCarrierId = targetCarrier.getInstanceId(); // 目标晶舟的ID
            Carrier sourceCarrier = getCarrier(new Carrier(sourceCarrierRrn)); // 源晶舟

            //1、 将交换后的晶圆数据 以新的carrierMappingRrn插入到 CARRIER_MAPPING 及其历史表中
            insertCarrierMapping(transRrn, targetCarrierRrn, carrierMappings, newCarrierMappingRrn);
            //2、 更新 目标晶舟 数据
            updateCarrier(transRrn, sourceCarrier.getPollutionLevel(), targetCarrierRrn, newCarrierMappingRrn, StringUtils.EMPTY, StringUtils.EMPTY, PcdStatus.IN_USE_KEY);
            //3、 删除原CarrierMapping映射关系的数据（并没有实际删除）
            deleteCarrierMapping(transRrn, sourceCarrierMapRrn);
            //4、 源晶舟的处理
            //MAINRC 和 原LOT使用同一个Cassette，即使MAINRC的UNIT交换出去以后，但是原LOT的UNIT还在，所以不能修改Cassette的状态
            PcdAssembly oldPcdAssembly = pcdManager.getPcdAssembly(sourceCarrierRrn);
            String sourceCarrierChangeStatus = (oldPcdAssembly == null) ? PcdStatus.FREE_KEY : PcdStatus.ASSEMBLY_KEY;
            PcdClean cleanInfo = null;
            if (needDealSourceCarrier) {
                carrierDAO.updateCarrierForInit(sourceCarrierRrn, sourceCarrierChangeStatus);//MAINRC不能执行
                carrierDAO.insertCarrierH(sourceCarrierRrn, "Init cassette", targetCarrierId,
                                          new TransactionLog(transRrn, LocalContext.getUserRrn()));
                cleanInfo = pcdManager.getPcdCleanInfo(sourceCarrierRrn);
                if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
                    pcdManager.insertPcdCleanH(transRrn, cleanInfo);
                }
            }
            if (StringUtils.equalsIgnoreCase(PcdStatus.WAIT_CLEAN_KEY, sourceCarrier.getCarrierStatus())) {
                if (needDealSourceCarrier) {
                    carrierDAO.updateCarrierForInit(sourceCarrierRrn, PcdStatus.WAIT_CLEAN_KEY);//MAINRC不能执行
                    carrierDAO.insertCarrierH(sourceCarrierRrn, "Init cassette", targetCarrierId,
                                              new TransactionLog(transRrn, LocalContext.getUserRrn()));
                    if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
                        pcdManager.insertPcdCleanH(transRrn, cleanInfo);
                    }
                }
            }
            // else {
                // if (needDealSourceCarrier) carrierDAO.updateCarrierForInit(sourceCarrierRrn, sourceCarrierChangeStatus);//MAINRC不能执行
                // carrierDAO.insertCarrierH(sourceCarrierRrn, "Init cassette", targetCarrierId,
                //                           new TransactionLog(transRrn, LocalContext.getUserRrn()));
                // PcdClean cleanInfo = pcdManager.getPcdCleanInfo(sourceCarrierRrn);
                // if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
                //     pcdManager.insertPcdCleanH(transRrn, cleanInfo);
                // }
            // }

            //5、 update SRC Lot And Units
            List<Long> inUseLotRrns = carrierDAO.getInUseCarrierLotRrnList(sourceCarrierRrn);
            //母RC的LOT表中的数据不能修改，子RC的LOT表中的数据需要修改。
            Assert.state(CollectionUtils.isNotEmpty(inUseLotRrns), Errors.create().key(MessageIdList.UNIT_DATA_ERROR).build());
            for (Long lotRrn : inUseLotRrns){
                Lot rcLot = lotQueryManager.getLot(lotRrn);
                if (LotStatus.isRunCardHold(rcLot.getLotStatus())) {
                    rcLot = lotQueryManager.getLot(RunCardUtils.buildMainRcLotId(rcLot.getLotId()),
                                                   rcLot.getFacilityRrn());
                }
                carrierDAO.updateLotForCarrierRrn(rcLot.getLotRrn(), targetCarrierRrn);
                carrierDAO.updateSrcLotForCarrierRrn(rcLot.getLotRrn(), targetCarrierRrn, targetCarrierId);
            }
            carrierDAO.updateUnitListForCarrierRrn(carrierMappings, sourceCarrierRrn, targetCarrierRrn);//如果是MAINRC，将指定的UNIT的carrierRRN修改，不能连原LOT的一起修改。
            lot.setCarrierMapRrn(newCarrierMappingRrn);
            lot.setCarrierRrn(targetCarrierRrn);
            createLotTransHistoryForExchanegCarrier(transRrn, transSeq, lot);
        }

        @Override
        public Carrier getCarrier(Carrier carrier) {
            NamedObject namedObject = objectManager.getNamedObject(carrier);
            if (namedObject == null || !StringUtils.equalsIgnoreCase(ObjectList.CARRIER_KEY, namedObject.getObjectType())) {
                return null;
            }
            carrier.copyNamedObject(namedObject);

            carrier = carrierDAO.getCarrier(carrier);
            // 补充getInstanceId
            if (carrier.getMcsLastOperatorRrn() != null) {
                carrier.setMcsLastOperator(objectManager.getInstanceId(carrier.getMcsLastOperatorRrn()));
            }
            return carrier;
        }

        @Override
        public Carrier getCarrier(Long facilityRrn, String carrierId) {
            Carrier carrier = getCarrier(
                    new Carrier(carrierId, objectManager.getNamedSpace(facilityRrn, ObjectList.FACILITY_KEY),
                                ObjectList.ENTITY_KEY));
            Assert.isTrue(Objects.nonNull(carrier) && carrier.getInstanceRrn() > 0,
                          Errors.create().content("Cassette Id: {} is not Exist").args(carrierId).build());
            return carrier;
        }

        @Override
        public Map<String, Object> getCarrierInfoDetailById(String carrierId, String namedSpace) {
            Map<String, Object> dataMap = carrierDAO.qryCarrierInfoDetailById(carrierId, namedSpace);
            PcdAssembly pcdAssembly = pcdManager.getPcdAssembly(MapUtils.getLong(dataMap, "carrierRrn"), null, null);
            if (pcdAssembly != null) {
                dataMap.put("podId", pcdAssembly.getPodId());
                dataMap.put("doorId", pcdAssembly.getDoorId());

                if (pcdManager.isPcdCleaningTimeIsLessThan3Days(pcdAssembly.getPodRrn())) {
                    dataMap.put("podCleanRedFlag", "1");
                }
                if (pcdManager.isPcdCleaningTimeIsLessThan3Days(pcdAssembly.getDoorRrn())) {
                    dataMap.put("doorCleanRedFlag", "1");
                }
            }
            if (pcdManager.isPcdCleaningTimeIsLessThan3Days(MapUtils.getLong(dataMap, "carrierRrn"))) {
                dataMap.put("carrierCleanRedFlag", "1");
            }
            return dataMap;
        }

        @Override
        public Map<String, Object> qryCarrierListByPage(Map<String, Object> argMap) {
            int pageSize = 20;
            int startRow = MapUtils.getInteger(argMap, "startRow", 0);
            if (argMap.get("pageSize") != null) {
                pageSize = MapUtils.getInteger(argMap, "pageSize", pageSize);
            }
            if (MapUtils.getLong(argMap, "facility") > 0) {
                argMap.put("namedSpace",
                           objectManager.getNamedSpace(MapUtils.getLong(argMap, "facility"), ObjectList.ENTITY_KEY));
            }

            if (StringUtils.isNotBlank(MapUtils.getString(argMap, "carrierId"))) {
                argMap.put("carrierId",
                           StringUtils.replace(MapUtils.getString(argMap, "carrierId"), "*", "%").toString().toUpperCase());
            }
            // 查询数据集和数据总量
            Page page = new Page((startRow / pageSize) + 1, pageSize);
            page = carrierDAO.getCarrierListByPage(page, argMap);
            int totalCount = (int) page.getTotalItems();
            List<Map> eqptInfoList = (List<Map>) page.getResults();

            for (Map dataMap : eqptInfoList) {
                // 替换location
                String location = MapUtils.getString(dataMap, "location");
                if (StringUtils.isNotBlank(location) && StringUtils.equalsIgnoreCase("$EQPT_LOCATION", location)) {
                    dataMap.put("location", "");
                }

                PcdAssembly pcdAssembly = pcdManager.getPcdAssembly(MapUtils.getLong(dataMap, "carrierRrn"), null, null);
                if (pcdAssembly != null) {
                    dataMap.put("podId", pcdAssembly.getPodId());
                    dataMap.put("doorId", pcdAssembly.getDoorId());

                    if (pcdManager.isPcdCleaningTimeIsLessThan3Days(pcdAssembly.getPodRrn())) {
                        dataMap.put("podCleanRedFlag", "1");
                    }
                    if (pcdManager.isPcdCleaningTimeIsLessThan3Days(pcdAssembly.getDoorRrn())) {
                        dataMap.put("doorCleanRedFlag", "1");
                    }
                }
                if (pcdManager.isPcdCleaningTimeIsLessThan3Days(MapUtils.getLong(dataMap, "carrierRrn"))) {
                    dataMap.put("carrierCleanRedFlag", "1");
                }
                // carrier query 增加 查询location、pLocation
                String carrierId = MapUtils.getString(dataMap, "carrierId", StringUtils.EMPTY);
                List<Lot> lotList = lotInqManager.getLotListByCarrierId(carrierId);
                if (CollectionUtils.isNotEmpty(lotList)) {
                    Set<String> locationIds = new HashSet<>();
                    Set<String> pLocationIds = new HashSet<>();
                    for (Lot lot : lotList) {
                        locationIds.add(lot.getLocation());
                        pLocationIds.add(lot.getpLocation());
                    }
                    String locationId = locationIds.stream().collect(Collectors.joining(StringUtils.COMMA_SIGN));
                    String pLocationId = pLocationIds.stream().collect(Collectors.joining(StringUtils.COMMA_SIGN));
                    dataMap.put("locationId", locationId);
                    dataMap.put("pLocationId", pLocationId);
                }
            }

            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("data", eqptInfoList);
            dataMap.put("totalCount", totalCount);
            return dataMap;
        }

        @Override
        public Carrier getCarrier(long carrierRrn) {
            return carrierDAO.getCarrier(carrierRrn);
        }

        @Override
        public void deleteCarrierEntity(String carrierId, Long facilityRrn, String user) {
            String namedSpace = objectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY);
            long carrierRrn = 0L;
            carrierRrn = objectManager.getNamedObjectRrn(carrierId, namedSpace, ObjectList.ENTITY_KEY,
                                                         ObjectList.CARRIER_KEY);

            Assert.isFalse(carrierRrn <= 0,
                           Errors.create().key(MessageIdList.CARRIER_CASSETTE_ID_NOT_EXIST).content("Cassette号不存在!")
                                 .build());

            // carrier存在，检查carrier当前信息，判断是否可以删除
            Carrier carrier = this.getCarrier(carrierRrn);
            Assert.isFalse(checkCarrierDelete(carrierRrn),
                           Errors.create().key(MessageIdList.CARRIER_IN_HISTORY_CANNOT_DELETE)
                                 .content("Carrier cannot delete a wafer that has been loaded").build());

            // 目前判断不是FREE状态不能删除
            if (StringUtils.equalsIgnoreCase(PcdStatus.FREE_KEY, carrier.getCarrierStatus())) {
                // 删除carrier
                carrier.setTransId("DELETE");
                carrier.setCarrierStatus(PcdStatus.DELETED_KEY);
                carrier.setTransPerformedby(user);

                deleteCarrier(carrier);
            } else {
                Assert.isFalse(StringUtils.equalsIgnoreCase(PcdStatus.HOLD_KEY, carrier.getCarrierStatus()),
                               Errors.create().key(MessageIdList.CARRIER_IN_HOLD_STATUS).content("晶舟在hold 状态，不能删除!")
                                     .build());
                throw new SystemIllegalArgumentException(
                        Errors.create().key(MessageIdList.CARRIER_IN_USE_CANNOT_DELETE).content("Carrier正在使用中，不能删除!")
                              .build());
            }
        }

        @Override
        public void deleteCarrier(Long transRrn, Carrier carrier, TransactionLog transactionLog) {
            carrierDAO.insertCarrierH(carrier.getInstanceRrn(), StringUtils.EMPTY, carrier.getInstanceId(), transactionLog);
            carrierDAO.deleteCarrier(carrier);
            long instanceRrn = objectManager.deleteNamedObject(carrier, transRrn);
            pcdManager.deletePcdCleanInfo(carrier.getInstanceRrn(), transRrn);
            //delete entity
            entityManager.deleteEntityOnly(instanceRrn);
            //delete entityDocument
            entityManager.deleteEntityDocs(instanceRrn);
            //delete entityConstraint
            entityManager.deleteEntityConstraints(instanceRrn);
        }

        @Override
        public void deleteCarrier(Carrier carrier) {
            // get the transaction log
            TransactionLog transactionLog = transactionLogManager.startTransactionLog(carrier.getTransPerformedby(),
                                                                                      carrier.getTransId());

            // delete the named object.
            long transRrn = transactionLog.getTransRrn();
            deleteCarrier(transRrn, carrier, transactionLog);

            // insert into Transaction Log
            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public void addCarrierEntity(Map<String, Object> attrMap, Long facilityRrn, String user) {

            String allowableEventId = MapUtils.getString(attrMap, "carrierAllowableEvent").toUpperCase().trim();
            String engineerGroupId = MapUtils.getString(attrMap, "carrierEngineerGroupID").toUpperCase().trim();
            long allowableEventRrn = objectManager.getNamedObjectRrn(allowableEventId,
                                                                     objectManager.getNamedSpace(facilityRrn,
                                                                                                 ObjectList.ALLOWABLEEVENTSET_KEY),
                                                                     ObjectList.ALLOWABLEEVENTSET_KEY.toString());
            long engineerGroupRrn = objectManager.getNamedObjectRrn(engineerGroupId,
                                                                    objectManager.getNamedSpace(facilityRrn,
                                                                                                ObjectList.USERGROUP_KEY),
                                                                    ObjectList.USERGROUP_KEY);

            Assert.isFalse(allowableEventRrn < 1,
                           Errors.create().key(MessageIdList.EVENT_ALLOWABLE_EVENT_MISSING).content("允许事件集无效!").build());
            Assert.isFalse(StringUtils.isNotBlank(engineerGroupId) && engineerGroupRrn < 1,
                           Errors.create().key(MessageIdList.ENGINEERGROUP_ENGINEER_GROUP_MISSING).content("工程师组无效!")
                                 .build());

            Carrier carrier = new Carrier();
            carrier.setAllowableEventsId(allowableEventId);
            carrier.setAllowableEventsRrn(allowableEventRrn);
            carrier.setCarrierCode(MapUtils.getString(attrMap, "carrierColor"));
            carrier.setInstanceId(MapUtils.getString(attrMap, "carrierId").toUpperCase().trim());
            carrier.setInstanceDesc(MapUtils.getString(attrMap, "carrierDesc"));
            carrier.setSlotCount(MapUtils.getLong(attrMap, "carrierSlotCount"));
            carrier.setAvailableSlotCount(MapUtils.getLong(attrMap, "carrierSlotCount"));
            carrier.setInstanceStatus("ACTIVE");
            carrier.setNamedSpace(objectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY));
            carrier.setObject(ObjectList.ENTITY_KEY);
            carrier.setObjectType(ObjectList.CARRIER_KEY);
            carrier.setPollutionLevel(MapUtils.getString(attrMap, "carrierPollutionLevel"));
            carrier.setCarrierStatus(PcdStatus.FREE_KEY);
            carrier.setFlagType(MapUtils.getString(attrMap, "carrierCategory"));

            String locationId = StringUtils.trimToUpperCase(MapUtils.getString(attrMap, "carrierlocation"));
            if (StringUtils.isNotBlank(locationId)) {
                long locationRrn = objectManager.getNamedObjectRrn(locationId, ObjectList.LOCATION_KEY,
                                                                   objectManager.getNamedSpace(facilityRrn,
                                                                                               ObjectList.LOCATION_KEY));

                Assert.isFalse(locationRrn <= 0,
                               Errors.create().key(MessageIdList.ENTITY_LOCATION_MISSING).content("位置无效!").build());
                carrier.setLocationRrn(locationRrn);

            }

            carrier.setTransId(TransactionNames.CREATE_KEY);
            carrier.setTransPerformedby(user);
            carrier.setObjectSubtype(MapUtils.getString(attrMap, "carrierType"));
            carrier.setMaintenanceEngineerId(engineerGroupId);
            carrier.setMaintenanceEngineerRrn(engineerGroupRrn);
            carrier.setFacilityRrn(facilityRrn);
            carrier.setCleanCycle(MapUtils.getDoubleValue(attrMap, "carrierCleanCycle"));

            insertCarrier(carrier);
        }

        @Override
        public long insertCarrier(Carrier carrier) {
            long instanceRrn = 0;
            // named object inserting and transaction start to log
            TransactionLog transactionLog = transactionLogManager.startTransactionLog(carrier.getTransPerformedby(),
                                                                                      carrier.getTransId());
            instanceRrn = objectManager.insertNamedObject(carrier, transactionLog.getTransRrn());
            carrier.setInstanceRrn(instanceRrn);
            pcdManager.insertPcdNamedObjectExt(instanceRrn, carrier.getFlagType(), carrier.getDmmType());
            entityManager.insertParentEntity(carrier, transactionLog.getTransRrn(), instanceRrn);
            entityManager.insertEntityHistory(carrier, transactionLog.getTransRrn(), instanceRrn);
            //Use the reconstructed insert and insert history method
            carrierDAO.insertCarrier(carrier);
            carrierDAO.insertCarrierH(instanceRrn, StringUtils.EMPTY, carrier.getInstanceId(), transactionLog);

            pcdManager.addPcdCleanInfo(instanceRrn, transactionLog.getTransRrn(), carrier.getFacilityRrn(),
                                       ObjectList.CARRIER_KEY, carrier.getCleanCycle());

            // mark transaction end flag
            transactionLogManager.markTransactionLog(transactionLog);
            return instanceRrn;
        }

        @Override
        public void updateCarrierEntity(Map<String, Object> attrMap, Long facilityRrn, String user) {
            String allowableEventId = MapUtils.getString(attrMap, "carrierAllowableEvent").toUpperCase().trim();
            String engineerGroupId = MapUtils.getString(attrMap, "carrierEngineerGroupID").toUpperCase().trim();
            long allowableEventRrn = objectManager.getNamedObjectRrn(allowableEventId,
                                                                     objectManager.getNamedSpace(facilityRrn,
                                                                                                 ObjectList.ALLOWABLEEVENTSET_KEY),
                                                                     ObjectList.ALLOWABLEEVENTSET_KEY.toString());

            long engineerGroupRrn = objectManager.getNamedObjectRrn(engineerGroupId,
                                                                    objectManager.getNamedSpace(facilityRrn,
                                                                                                ObjectList.USERGROUP_KEY),
                                                                    ObjectList.USERGROUP_KEY);

            Assert.isFalse(allowableEventRrn <= 0,
                           Errors.create().key(MessageIdList.EVENT_ALLOWABLE_EVENT_MISSING).content("允许事件集无效!").build());
            Assert.isFalse(StringUtils.isNotBlank(engineerGroupId) && engineerGroupRrn < 1,
                           Errors.create().key(MessageIdList.ENGINEERGROUP_ENGINEER_GROUP_MISSING).content("工程师组无效!")
                                 .build());

            Long carrierRrn = MapUtils.getLong(attrMap, "carrierRrn");

            Carrier carrier = getCarrier(carrierRrn);

            carrier.setAllowableEventsId(allowableEventId);
            carrier.setAllowableEventsRrn(allowableEventRrn);
            carrier.setCarrierCode(MapUtils.getString(attrMap, "carrierColor"));
            carrier.setInstanceDesc(MapUtils.getString(attrMap, "carrierDesc"));
            carrier.setPollutionLevel(MapUtils.getString(attrMap, "carrierPollutionLevel"));
            carrier.setFlagType(MapUtils.getString(attrMap, "carrierCategory"));

            String locationId = StringUtils.trimToUpperCase(MapUtils.getString(attrMap, "carrierlocation"));
            if (StringUtils.isNotBlank(locationId)) {
                long locationRrn = objectManager.getNamedObjectRrn(locationId, ObjectList.LOCATION_KEY,
                                                                   objectManager.getNamedSpace(facilityRrn,
                                                                                               ObjectList.LOCATION_KEY));
                Assert.isFalse(locationRrn <= 0,
                               Errors.create().key(MessageIdList.ENTITY_LOCATION_MISSING).content("位置无效!").build());
                carrier.setLocationRrn(locationRrn);
            }

            carrier.setTransId(TransactionNames.MODIFY_KEY);
            carrier.setTransPerformedby(user);
            carrier.setObjectSubtype(MapUtils.getString(attrMap, "carrierType"));
            carrier.setMaintenanceEngineerId(engineerGroupId);
            carrier.setMaintenanceEngineerRrn(engineerGroupRrn);
            carrier.setSlotCount(MapUtils.getLong(attrMap, "carrierSlotCount"));
            carrier.setAvailableSlotCount(MapUtils.getLong(attrMap, "carrierAvailableSlotCount"));

            TransactionLog transactionLog = transactionLogManager.startTransactionLog(user, carrier.getTransId());
            long transRrn = transactionLog.getTransRrn();

            if (pcdManager.countPcdCategoryByRrn(carrier.getInstanceRrn()) > 0) {
                // 已经存在数据
                pcdManager.updatePcdNamedObjectExt(carrier.getInstanceRrn(), carrier.getFlagType());
            } else {
                pcdManager.insertPcdNamedObjectExt(carrier.getInstanceRrn(), carrier.getFlagType(), carrier.getDmmType());
            }
            carrier.setLastUpdateTimestamp(transactionLog.getTransStartTimestamp());
            carrier.setLastUpdateUserRrn(LocalContext.getUserRrn());
            carrierDAO.updateCarrier(carrier, transRrn);
            PcdClean carrierCleanInfo = new PcdClean();
            carrierCleanInfo.setInstanceRrn(carrier.getInstanceRrn());
            carrierCleanInfo.setInstanceId(carrier.getInstanceId());
            carrierCleanInfo.setCleanCycle(MapUtils.getDoubleValue(attrMap, "carrierCleanCycle"));
            carrierCleanInfo.setNamedSpace(objectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY));
            pcdManager.updatePcdCleanInfo(transRrn, carrierCleanInfo);

            // insert into Transaction Log
            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public Map<String, Object> qryCarrierInfoDetailById(String carrierId, String namedSpace) {
            return carrierDAO.qryCarrierInfoDetailById(carrierId, namedSpace);
        }

        @Override
        public void insertCarrierH(Long carrierRrn, String comments, Long transRrn) {
            carrierDAO.insertCarrierH(carrierRrn, comments, StringUtils.EMPTY,
                                      new TransactionLog(transRrn, LocalContext.getUserRrn()));
        }

        @Override
        public void updateCarrierAvailableSlotCount(Long carrierRrn) {
            carrierDAO.updateCarrierAvailableSlotCount(carrierRrn);
        }

        @Override
        public void holdCarrier(Map transInfo) {
            String transPerformedBy = MapUtils.getString(transInfo, "transPerformedBy");
            long holdBy = MapUtils.getLong(transInfo, "holdBy");
            NamedObject pcd = (NamedObject) transInfo.get("pcd");
            long instanceRrn = pcd.getInstanceRrn();
            long facilityRrn = MapUtils.getLongValue(transInfo, "facilityRrn");
            TransReason transReason = (TransReason) transInfo.get("transReason");

            TransactionLog transactionLog = transactionLogManager.startTransactionLog(transPerformedBy,
                                                                                      TransactionNames.HOLD_CARRIER_KEY);
            long transRrn = transactionLog.getTransRrn();

            Carrier carrier = getCarrier(instanceRrn);
            if (!StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.HOLD_KEY)) {
                // step1: update pcd status and add history
                String cstEventId = eventManager.getEventIdBy2Status(facilityRrn, instanceRrn, carrier.getCarrierStatus(),
                                                                     PcdStatus.HOLD_KEY);
                statusManager.changePCDStatusByEvent(facilityRrn, transPerformedBy, instanceRrn, ObjectList.CARRIER_KEY,
                                                     cstEventId, "Hold");

                updateCarrierStatus(transactionLog, instanceRrn, carrier.getInstanceId(), PcdStatus.HOLD_KEY);

                // step2: update pod and door for assembled casset
                PcdAssembly pcdAssembly = pcdManager.getPcdAssembly(instanceRrn);
                if (pcdAssembly != null) {

                    transInfo.put("pcdAssembly", pcdAssembly);
                    if (pcdAssembly.getPodRrn() != null && pcdAssembly.getPodRrn() > 0) {
                        NamedObject pod = objectManager.getNamedObject(pcdAssembly.getPodRrn());
                        transInfo.put("pcd", pod);
                        podManager.holdPod(transInfo);
                    }
                    if (pcdAssembly.getDoorRrn() != null && pcdAssembly.getDoorRrn() > 0) {
                        NamedObject door = objectManager.getNamedObject(pcdAssembly.getDoorRrn());
                        transInfo.put("pcd", door);
                        doorManager.holdDoor(transInfo);
                    }
                }
            }

            // step3: insert into multiple_Hold
            pcdManager.insertMultipleHold(instanceRrn, holdBy, transRrn);

            // step3: insert into trans_reason
            pcdManager.insertTransReason(transRrn, instanceRrn, transReason, 1);
            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public void releaseCarrier(Map transInfo) {
            String transPerformedBy = MapUtils.getString(transInfo, "transPerformedBy");
            List releaseReasons = (List) transInfo.get("releaseReasons");
            NamedObject pcd = (NamedObject) transInfo.get("pcd");
            long instanceRrn = pcd.getInstanceRrn();
            String transNames = TransactionNames.RELEASE_CARRIER_KEY;
            TransReason transReason = (TransReason) transInfo.get("transReason");
            String targetStatus = MapUtils.getString(transInfo, "targetStatus");
            long facilityRrn = MapUtils.getLongValue(transInfo, "facilityRrn");
            TransactionLog transactionLog = transactionLogManager.startTransactionLog(transPerformedBy, transNames);

            // delete Multiple hold
            Object[] releaseReasonArray = releaseReasons.toArray();

            for (int i = 0; i < releaseReasonArray.length; i++) {
                HashMap releaseReason = (HashMap) releaseReasonArray[i];
                pcdManager.deleteMultipleHold(instanceRrn, Long.parseLong((String) releaseReason.get("sequenceNumber")));
            }

            if (!StringUtils.equalsIgnoreCase(targetStatus, PcdStatus.HOLD_KEY)) {
                Carrier carrier = getCarrier(instanceRrn);
                String cstEventId = eventManager.getEventIdBy2Status(facilityRrn, instanceRrn, carrier.getCarrierStatus(),
                                                                     targetStatus);
                statusManager.changePCDStatusByEvent(facilityRrn, transPerformedBy, instanceRrn, ObjectList.CARRIER_KEY,
                                                     cstEventId, "Hold");

                updateCarrierStatus(transactionLog, instanceRrn, carrier.getInstanceId(), targetStatus);
                PcdAssembly pcdAssembly = pcdManager.getPcdAssembly(instanceRrn);

                if (pcdAssembly != null) {
                    transInfo.put("pcdAssembly", pcdAssembly);
                    if (pcdAssembly.getPodRrn() != null && pcdAssembly.getPodRrn() > 0) {
                        NamedObject pod = objectManager.getNamedObject(pcdAssembly.getPodRrn());
                        transInfo.put("pcd", pod);
                        podManager.releasePod(transInfo);
                    }
                    if (pcdAssembly.getDoorRrn() != null && pcdAssembly.getDoorRrn() > 0) {
                        NamedObject door = objectManager.getNamedObject(pcdAssembly.getDoorRrn());
                        transInfo.put("pcd", door);
                        doorManager.releaseDoor(transInfo);
                    }
                }
            }

            transReason.setReasonCategory(transNames);

            pcdManager.insertTransReason(transactionLog.getTransRrn(), instanceRrn, transReason, 1);

            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public Page queryCarrierHistory(Page page, Map<String, Object> query) {
            // 处理时间
            String dueDateS = MapUtils.getString(query, "dueDateS");
            String dueDateE = MapUtils.getString(query, "dueDateE");
            if (StringUtils.isNotBlank(dueDateS)) {
                dueDateS += " 00:00:00";
                query.put("dueDateS", dueDateS);
            }
            if (StringUtils.isNotBlank(dueDateE)) {
                dueDateE += " 23:59:59";
                query.put("dueDateE", dueDateE);
            }

            return carrierDAO.queryCarrierHistory(page, query);

        }

        @Override
        public List<Map> getAllCarrier(Map<String, Object> getAllCarrierMap) {
            //        return carrierDAO.getCarrierListByPage(getAllCarrierMap, 0, Integer.MAX_VALUE);
            return (List<Map>) carrierDAO.getCarrierListByPage(new Page(1, Integer.MAX_VALUE), getAllCarrierMap)
                                         .getResults();
        }

        @Override
        public List<String> getInUseCarrierLotIdList(Long carrierRrn) {
            return carrierDAO.getInUseCarrierLotIdList(carrierRrn);
        }

        @Override
        public Long getNumberOfAvailableInCarrier(Long carrierRrn) {
            return carrierDAO.getNumberOfAvailableInCarrier(carrierRrn);
        }

        @Override
        public Long getAvailableCarrierRrnById(Long facilityRrn, String carrierId) {
            Assert.isFalse(StringUtils.isEmpty(carrierId),
                           Errors.create().content("Cassette Id can not be Empty!").build());
            String carrierIdUpper = carrierId.trim().toUpperCase();
            long carrierRrn = getCarrier(facilityRrn, carrierIdUpper).getInstanceRrn();
            checkIsInUseCarrier(carrierIdUpper, carrierRrn);
            return carrierRrn;
        }

        @Override
        public void exchangeCarrierSpecial(Lot lot, long toCarrierRrn, List<Map> carrierMappings, Carrier toCarrier,
                                           Long facilityRrn, String userId) {
            // 批次原晶舟
            pcdManager.changeStatusFromInUseToFreeOrAssembly(lot.getFacilityRrn(), lot.getTransPerformedby(),
                                                             lot.getCarrierRrn(), "Release for exchange cassette");
            // 批次新晶舟
            pcdManager.changeStatusFromFreeOrAssemblyToInUse(facilityRrn, userId, toCarrier.getInstanceRrn(),
                                                             "In use for exchange cassette");
            TransactionLog transactionLog = transactionLogManager.startTransactionLog(lot.getTransPerformedby(),
                                                                                      lot.getTransId());

            exchangeCarrier(transactionLog.getTransRrn(), 1L, lot, toCarrierRrn, carrierMappings);

            transactionLogManager.markTransactionLog(transactionLog);
        }

        /**
         * RunCard Lot的 Exchange Cassette
         * @param lot           必须为MainRC在Lot表中的Lot对象
         * @param toCarrierRrn
         * @param carrierMappings
         * @param toCarrier
         * @param facilityRrn
         * @param userId
         */
        @Override
        public void exchangeCarrierSpecial4Src(Lot lot, long toCarrierRrn, List<Map> carrierMappings, Carrier toCarrier,
                                               Long facilityRrn, String userId) {
            Long sourceCarrierRrn,sourceCarrierMapRrn;
            boolean needDealSourceCarrier = true;
            if (StringUtils.endsWith(lot.getLotId(), RunCardConstants.MAINLOT_ID_IN_MES)) {
                //MAINRC的Lot
                Map<String, Object> rcLotInfo = runCardQueryManager.getRCLotInfoMap(lot.getLotId());
                Lot lotForLotTable = lotQueryManager.getLot(lot.getLotRrn());
                long sourceCarrierRrnForLotTable = lotForLotTable.getCarrierRrn();
                sourceCarrierRrn = MapUtils.getLong(rcLotInfo, "carrierRrn", -1L);
                boolean isNotSame = sourceCarrierRrn != sourceCarrierRrnForLotTable;//不一致只有一种场景，就是MainRC和原LOT使用同一个Cassette时。
                Carrier sourceCarrier = carrierDAO.getCarrier(sourceCarrierRrn);
                sourceCarrierMapRrn = MapUtils.getLong(rcLotInfo, "carrierMapRrn", -1L);
                if (isNotSame || CarrierType.DUMMY.equalsIgnoreCase(sourceCarrier.getObjectSubtype())) {
                    //MAINRC 的Cassette在和原LOT使用同一Cassette时，即真实Cassette是个虚拟Cassette时，不需要sourceCarrier切换状态，只需要把它剥离出去即可。
                    // 不使用虚拟Cassette时，代表已经被交换过了，则交换后需要处理原Cassette。
                    needDealSourceCarrier = false;
                }
            } else {
                sourceCarrierRrn = lot.getCarrierRrn();
                sourceCarrierMapRrn = lot.getCarrierMapRrn();
            }

            if (needDealSourceCarrier) {
                // 源晶舟
                pcdManager.changeStatusFromInUseToFreeOrAssembly(lot.getFacilityRrn(), lot.getTransPerformedby(), sourceCarrierRrn, "Release for exchange cassette");
            }
            // 批次新晶舟
            pcdManager.changeStatusFromFreeOrAssemblyToInUse(facilityRrn, userId, toCarrier.getInstanceRrn(), "In use for exchange cassette");

            TransactionLog transactionLog = transactionLogManager.startTransactionLog(lot.getTransPerformedby(), lot.getTransId());

            exchangeCarrier4Src(transactionLog.getTransRrn(), 1L, lot, sourceCarrierRrn, sourceCarrierMapRrn, toCarrierRrn, carrierMappings, needDealSourceCarrier);

            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public boolean isPcdType(String carrierType) {
            String result = carrierDAO.getCarrierTypeIsPcdOrOpen(carrierType);
            if (StringUtils.isNotEmpty(result)) {
                if (StringUtils.equalsIgnoreCase(result, "1")) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public Long createCarrierMapping(long transRrn, Carrier carrier, List<Map> units) {

            Long carrierMappingRrn = null;

            if (!units.isEmpty()) {
                carrierMappingRrn = NumberUtils.toLong(
                        IDGenerators.get(IDNames.SEQUENCE).generateId(SequenceNames.MODULE_SEQ_OBJECT_RRN));
                // 母批记录
                // insertCarrierMappingH(transRrn, carrier.getCarrierMapRrn());
                // 母批批操作与记录
                insertCarrierMapping(transRrn, carrier.getInstanceRrn(), units, carrierMappingRrn);
                updateCarrier(transRrn, carrier.getPollutionLevel(), carrier.getInstanceRrn(), carrierMappingRrn,
                              carrier.getLotId(), "", PcdStatus.IN_USE_KEY);

                PcdAssembly pcdAssembly = pcdManager.getPcdAssembly(carrier.getInstanceRrn());
                if (pcdAssembly != null) {
                    this.updatePodAndDoorStatus(pcdAssembly, transRrn, PcdStatus.IN_USE_KEY);
                }
            }
            return carrierMappingRrn;
        }

        @Override
        public Long getCarrierRrnForLot(Long facilityRrn, Lot lot, String carrierId) {
            carrierId = StringUtils.trimToUpperCase(carrierId);
            Assert.isFalse(StringUtils.isEmpty(carrierId), Errors.create().content("Cassette is required!").build());

            Carrier carrier = getCarrier(facilityRrn, carrierId);

            Assert.isFalse(StringUtils.equalsIgnoreCase(carrier.getObjectSubtype(), Carrier.CarrierType.DUMMY.toString()),
                           Errors.create().content("The carrier: {} is a  dummy carrier!").args(carrier.getInstanceId())
                                 .build());
            Assert.isFalse(carrier.getAvailableSlotCount() < lot.getQty1(),
                           Errors.create().content("The AvailableSlot is not " + "enough!").build());
            Assert.isFalse(!StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.FREE_KEY) &&
                                   !StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.ASSEMBLY_KEY),
                           Errors.create().content("Invalid carrier status!").build());

            return getAvailableCarrierRrnById(facilityRrn, carrierId);
        }

        @Override
        public Long getCarrierRrnForLot(Long facilityRrn, Double lotQty, String carrierId) {
            carrierId = StringUtils.trimToUpperCase(carrierId);
            Assert.isFalse(StringUtils.isEmpty(carrierId), Errors.create().content("Cassette is required!").build());

            Carrier carrier = getCarrier(facilityRrn, carrierId);

            Assert.isFalse(StringUtils.equalsIgnoreCase(carrier.getObjectSubtype(), Carrier.CarrierType.DUMMY.toString()),
                           Errors.create().content("The carrier: {} is a  dummy carrier!").args(carrier.getInstanceId())
                                 .build());
            Assert.isFalse(carrier.getAvailableSlotCount() < lotQty,
                           Errors.create().content("The AvailableSlot is not enough!").build());
            Assert.isFalse(!StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.FREE_KEY) &&
                                   !StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.ASSEMBLY_KEY),
                           Errors.create().content("Invalid carrier status!").build());

            Assert.isFalse(isAssseblyNeed(carrier.getObjectSubtype()) &&
                                   !StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.ASSEMBLY_KEY),
                           Errors.create().content("{} type carrier needed assembled first!")
                                 .args(carrier.getObjectSubtype()).build());

            pcdManager.checkPcdIsValid(carrier.getInstanceRrn(), facilityRrn);

            return getAvailableCarrierRrnById(facilityRrn, carrierId);
        }

        @Override
        public Boolean isAssseblyNeed(String carrierType) {
            String assseblyNeed = StringUtils.EMPTY;
            assseblyNeed = referenceFileManager.getReferenceDetailExchange(ReferenceDetailNames.CARRIER_TYPE, carrierType,
                                                                           null, ReferenceFileConst.DATA_2_VALUE);
            return StringUtils.equalsIgnoreCase(assseblyNeed, "1");
        }

        @Override
        public String getTargetCarrierTypeByStatus(Lot lot) {
            if (StringUtils.equalsIgnoreCase(lot.getLotStatus(), LotStatus.RUNNING) ||
                    StringUtils.equalsIgnoreCase(lot.getLotStatus(), LotStatus.PROCESSED) ||
                    StringUtils.equalsIgnoreCase(lot.getLotStatus(), LotStatus.RUNNINGHOLD)) {
                return getToCarrierTypeLocation(lot);
            } else {
                return getFromCarriertypeLocation(lot);
            }
        }

        @Override
        public String getCarrierTypeByProcessLocation(String processLocation) {
            String carrierType = StringUtils.EMPTY;
            if (StringUtils.isNotBlank(processLocation)) {
                carrierType = referenceFileManager.getRefFileValue(ReferenceDetailNames.PROCESS_LOCARION_CARRIER_TYPE,
                                                                   processLocation, ReferenceFileConst.DATA_1_VALUE);
            }
            return carrierType == null ? StringUtils.EMPTY : carrierType;
        }

        @Override
        public String getCarriertypeByProcessLocation(Lot lot) {
            String carrierType = StringUtils.EMPTY;

            // 以情境值为准
            String processLocation = processLocationContextManager.getCurrentProcessLocation(LocalContext.getFacilityRrn(),
                                                                                          lot);

            if (StringUtils.isNotBlank(processLocation)) {
                carrierType = referenceFileManager.getRefFileValue(ReferenceDetailNames.PROCESS_LOCARION_CARRIER_TYPE,
                                                                   processLocation, ReferenceFileConst.DATA_1_VALUE);
            }
            return carrierType == null ? StringUtils.EMPTY : carrierType;
        }

        @Override
        public String getCarriertypeByProcessLocationContext(Lot lot) {
            String carrierType = StringUtils.EMPTY;

            String processLocation = processLocationContextManager.getCurrentProcessLocation(LocalContext.getFacilityRrn(),
                                                                                             lot);
            if (StringUtils.isBlank(processLocation)) {
                processLocation = processLocationContextManager.getCurrentProcessLocation(LocalContext.getFacilityRrn(),
                                                                                          lot);
            }

            if (StringUtils.isNotBlank(processLocation)) {
                carrierType = referenceFileManager.getRefFileValue(ReferenceDetailNames.PROCESS_LOCARION_CARRIER_TYPE,
                                                                   processLocation, ReferenceFileConst.DATA_1_VALUE);
            }
            return carrierType == null ? StringUtils.EMPTY : carrierType;
        }

        @Override
        public void dispatchCarrier(long carrierRrn, int consumedSlotCount) {
            carrierDAO.dispatchCarrier(carrierRrn, consumedSlotCount);
        }

        @Override
        public Long getCarrierMapRrn(Long carrierRrn) {
            return carrierDAO.getCarrierMapRrn(carrierRrn);
        }

        @Override
        public void dispatch(long lotRrn, long jobRrn, long eqptRrn) {
            carrierDAO.updateCarrierForDispatch(lotRrn, jobRrn, eqptRrn);
        }

        @Override
        public String getTargetCarrierTypeByActionPoint(String actionPoint, Lot lot) {
            if (StringUtils.equalsIgnoreCase(actionPoint, ActionPointList.MOVEIN_KEY)) {
                return getFromCarriertypeLocation(lot);
            } else {
                return getToCarrierTypeLocation(lot);
            }
        }

        @Override
        public boolean isThisCarrierPmNotMoveIn(long carrierRrn) {
            List<Map<String, Object>> list = carrierDAO.getCarrierChecklistJob(carrierRrn);
            for (Map<String, Object> map : list) {
                String state = MapUtils.getString(map, "checklistJobState");
                String actionFlag1 = MapUtils.getString(map, "actionFlag1");
                if (StringUtils.equalsIgnoreCase("STARTED", state) && StringUtils.equalsIgnoreCase("1", actionFlag1)) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public void makeCarrierRun(long carrierRrn, long runRrn, Short loadPosition) {
            carrierDAO.updateRunRrn(runRrn, carrierRrn);
            carrierDAO.updateLoadPosition(loadPosition, carrierRrn);
        }

        @Override
        public void updateCarrierPollutionLevel(String carrierId, String pollutionLevel, String lotId, long transRrn) {
            Carrier carrier = getCarrier(LocalContext.getFacilityRrn(), carrierId);
            if (carrier != null && !StringUtils.equalsIgnoreCase(pollutionLevel, carrier.getPollutionLevel())) {
                carrier.setPollutionLevel(pollutionLevel);
                carrier.setTransPerformedby(LocalContext.getUserId());
                carrier.setTransId(TransactionNames.MOVEOUT_KEY);
                carrier.setLotId(lotId);

                carrierDAO.updateCarrierPollutionLevel(carrier, transRrn);
            }
        }

        @Override
        public void insertCarrierMapping(Long transRrn, Long carrierRrn, List<Map> carrierMappings,
                                         Long carrierMappingRrn) {
            carrierDAO.insertBatchCarrierMapping(carrierMappingRrn, carrierRrn, carrierMappings);
            insertCarrierMappingH(transRrn, carrierMappingRrn);
        }

        @Override
        public void exchangeCarrierByCarrier(Lot lot, Long toCarrierRrn, List<Map> carrierMappings) {
            TransactionLog transactionLog = transactionLogManager.startTransactionLog(lot.getTransPerformedby(),
                                                                                      lot.getTransId());
            exchangeCarrier(transactionLog.getTransRrn(), 1L, lot, toCarrierRrn, carrierMappings);
            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public void checkPCDInfo(List<String> lotIdList, String actionPoint) {
            for (String lotId : lotIdList) {
                lotId = StringUtils.trimToUpperCase(lotId);
                Lot lot = lotQueryManager.getLot(lotId, LocalContext.getFacilityRrn());
                String targetCarrierType;
                Carrier carrier = getCarrier(new Carrier(lot.getCarrierRrn()));

                targetCarrierType = getTargetCarrierTypeByActionPoint(actionPoint, lot);

                Assert.isFalse(StringUtils.isEmpty(targetCarrierType),
                               Errors.create().key(MessageIdList.PROCESS_CARRIER_MUST_EMPTY)
                                     .content("Process location for {}  cannot be empty!").args(lot.getLotId()).build());
                Assert.isTrue(StringUtils.equalsIgnoreCase(carrier.getObjectSubtype(), targetCarrierType),
                              Errors.create().key(MessageIdList.PROCESS_NOT_MATCH_CASSETTE)
                                    .content("Cassette type for {} not match, Please exchange Cassette!")
                                    .args(lot.getLotId()).build());

                pcdManager.checkPcdIsValid(carrier.getInstanceRrn(), LocalContext.getFacilityRrn());
            }
        }

        @Override
        public void checkPcdInfo(List<Lot> lotList, String actionPoint) {
            for (Lot lot : lotList) {
                Carrier carrier = null;
                String targetCarrierType;

                if (RunCardUtils.checkLotIdIsRunCardLot(lot.getLotId())) {
                    RunCardLotInfo runCardLotInfo = runCardQueryManager.getRunCardLotInfo(lot.getLotId());
                    carrier = getCarrier(runCardLotInfo.getCarrierRrn());
                    String carrierTypeByProcessLocation = getCarrierTypeByProcessLocation(
                            runCardLotInfo.getProcessLocation());
                    targetCarrierType = getRunCardTargetCstType(actionPoint, carrierTypeByProcessLocation);
                } else {
                    carrier = getCarrier(new Carrier(lot.getCarrierRrn()));
                    targetCarrierType = getTargetCarrierTypeByActionPoint(actionPoint, lot);
                }

                Assert.isFalse(StringUtils.isEmpty(targetCarrierType),
                               Errors.create().key(MessageIdList.PROCESS_CARRIER_MUST_EMPTY)
                                     .content("Process location for {}  cannot be empty!").args(lot.getLotId()).build());
                Assert.isTrue(StringUtils.equalsIgnoreCase(carrier.getObjectSubtype(), targetCarrierType),
                              Errors.create().key(MessageIdList.PROCESS_NOT_MATCH_CASSETTE)
                                    .content("Cassette type for {} not match, Please exchange Cassette!")
                                    .args(lot.getLotId()).build());

                pcdManager.checkPcdIsValid(carrier.getInstanceRrn(), LocalContext.getFacilityRrn());
            }
        }

        /**
         * 兼容处理 AA-BB情况
         * @param actionPoint
         * @param carrierTypeByProcessLocation
         * @return
         */
        private String getRunCardTargetCstType(String actionPoint, String carrierTypeByProcessLocation) {
            String targetCarrierType = carrierTypeByProcessLocation;
            Matcher mathcher = PCDPATERN.matcher(carrierTypeByProcessLocation);
            Boolean match = mathcher.matches();
            if (match) {
                if (StringUtils.equalsIgnoreCase(actionPoint, ActionPointList.MOVEIN_KEY)) {
                    targetCarrierType = carrierTypeByProcessLocation.substring(0, carrierTypeByProcessLocation
                            .indexOf(CSTTYPE_SEPERATER_KEY));
                } else if (StringUtils.equalsIgnoreCase(actionPoint, ActionPointList.MOVEOUT_KEY)){
                    targetCarrierType = carrierTypeByProcessLocation
                            .substring(carrierTypeByProcessLocation.indexOf(CSTTYPE_SEPERATER_KEY) + 1);
                }
            }
            return targetCarrierType;
        }

        @Override
        public Long updateCarrierMapping(TransactionLog transactionLog, Long carrierRrn, List<Unit> unitList) {
            List<Map> unitMapList = unitList.stream().map(unit -> {
                Map unitMap = BeanUtils.copyBeanToMap(unit);
                unitMap.put("position", unit.getPositionInCarrier());
                unitMap.put("available", CheckRegexUtils.FLAG_ON);
                return unitMap;
            }).collect(Collectors.toList());

            Long carrierMappingRrn = updateCarrierMapping(transactionLog.getTransRrn(), carrierRrn, unitMapList);
            transactionLog.setTransSequence(transactionLog.getTransSequence() + 1);
            return carrierMappingRrn;
        }

        @Override
        public Long updateCarrierMapping(Long transRrn, Long carrierRrn, List<Map> carrierMappings) {
            Long carrierMappingRrn = null;
            if (!carrierMappings.isEmpty()) {
                carrierMappingRrn = new Long(
                        IDGenerators.get(IDNames.SEQUENCE).generateId(SequenceNames.MODULE_SEQ_OBJECT_RRN));
                // step 1. create new carrierMapping
                deleteCarrierMapping(transRrn, carrierRrn);
                this.insertCarrierMapping(transRrn, carrierRrn, carrierMappings, carrierMappingRrn);

                // step 2. update Lot And Units Carriers
                carrierDAO.updateUnitForCarrierMapping(carrierMappings);

                // step 3. update new carrier info
                carrierDAO.updateCarrierForCarrierMapRrn(carrierRrn, carrierMappings.size(), carrierMappingRrn);
                carrierDAO.insertCarrierH(carrierRrn, "", "", new TransactionLog(transRrn, LocalContext.getUserRrn()));
            } else {
                deleteCarrierMapping(transRrn, carrierRrn);
                carrierDAO.updateCarrierForCarrierMapRrn(carrierRrn, NumberUtils.INTEGER_ZERO, null);
            }
            return carrierMappingRrn;
        }

        @Override
        public Carrier setCarrierForVirtual(String userId, Long facilityRrn, String lotId, Double lotqty) {
            Carrier targetCarrier = new Carrier();
            targetCarrier.setInstanceId(Carrier.CarrierType.DUMMY.toString() + lotId);
            targetCarrier.setNamedSpace(objectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY));
            targetCarrier.setFacilityRrn(facilityRrn);
            targetCarrier.setObject(ObjectList.ENTITY_KEY);
            targetCarrier.setObjectType(ObjectList.CARRIER_KEY);
            targetCarrier.setObjectSubtype(Carrier.CarrierType.DUMMY.toString());
            targetCarrier.setAvailableSlotCount(SLOTMAXCOUNT - lotqty.longValue());
            targetCarrier.setTransId(TransactionNames.CREATE_KEY);
            targetCarrier.setTransPerformedby(userId);
            targetCarrier.setStatus(PcdStatus.FREE_KEY);

            return targetCarrier;
        }

        @Override
        public void exchangeCarrierByLot(Long transRrn, Long transSeq, Lot lot, Long newCarrierRrn) {
            exchangeCarrier(transRrn, transSeq, lot, newCarrierRrn, unitQueryManager.getUnitListByLot(lot.getLotRrn()));
        }

        @Override
        public Long getNumberOfUnitInCarrier(Long carrierRrn) {
            return carrierDAO.getNumberOfUnitInCarrier(carrierRrn);
        }

        @Override
        public List<Long> getInUseCarrierLotRrnList(Long carrierRrn) {
            return carrierDAO.getInUseCarrierLotRrnList(carrierRrn);
        }

        @Override
        public boolean checkCarrierDelete(long carrierRrn) {
            return carrierDAO.checkCarrierInCarrierMappingH(carrierRrn) || carrierDAO.checkCarrierInSortJob(carrierRrn);
        }

        @Override
        public Boolean getCarrierRefreshInfo(Long carrierRrn) {
            return carrierDAO.getCarrierRefreshInfo(carrierRrn);
        }

        @Override
        public void refreshCarrier(Long carrierRrn) {
            carrierDAO.refreshCarrier(carrierRrn);
        }

        @Override
        public String generateCstIdByType(String prefix, int width) {
            String newPodId = carrierDAO.generateCstIdByType(prefix, width);
            Assert.isFalse(StringUtils.contains(newPodId, "-1"),
                           Errors.create().key(MessageIdList.PCD_CASSETTE_OUT_OF_MAX).content("Error, Maximum upper limit")
                                 .build());
            return newPodId;
        }

        @Override
        public void exchangeCarrierType(Carrier carrier, String comments, Long transRrn, Boolean moveOutFlag) {
            if (moveOutFlag) {
                Lot lot = lotQueryManager.getLotByCarrierRrn(carrier.getInstanceRrn());
                lot.setTransId(TransactionNames.EXCHANGE_TYPE_KEY);
                lot.setTransPerformedby(carrier.getTransId());
                lot.setLotComments(comments);
                createLotTransHistoryForExchanegCarrier(transRrn, 1L, lot);
            }
            carrierDAO.updateCarrierType(carrier, comments, transRrn);
        }

        @Override
        public Boolean checkSwitchCarrierType(String processLocation) {
            String carrierType = "";
            if (StringUtils.isNotBlank(processLocation)) {
                carrierType = referenceFileManager.getRefFileValue(
                        ReferenceDetailNames.PROCESS_LOCATION_AUTO_CHANGE_CASSETTE_TYPE, processLocation,
                        ReferenceFileConst.DATA_1_VALUE);
                if (StringUtils.isNotEmpty(carrierType) && WipUtils.PCDPATERN.matcher(carrierType).matches()) {
                    return true;
                } else {
                    return false;
                }
            }
            return false;
        }

        @Override
        public String generatePcdIdByPcdType(String pcdType, String prefix, String ignorePrefix, int length) {
            List<String> ignorePrefixSetting = new ArrayList<>();
            if (StringUtils.isNotEmptyTrim(ignorePrefix)) {
                //用户手动定义的列表
                ignorePrefixSetting.addAll(StringUtils.splitAsList(ignorePrefix, LotConstants.DEFAULY_SEPARATOR));
                //过滤与 固定前缀相同的数据，过滤小于前缀的脏数据
                ignorePrefixSetting= ignorePrefixSetting.stream().filter((e) -> !(StringUtils.equalsIgnoreCase(prefix, e)
                        || e.length() < prefix.length())).distinct().collect(Collectors.toList());
            }

            //拿到库中最大lot seq序列
            String currentDBMaxLotSeq = carrierDAO.generatePcdIdByPcdType(pcdType, prefix, ignorePrefixSetting, length);// TODO 要把前缀 和 忽略前缀的字符抛开后的真实 Seq

            String createdLotSeq = SystemConstant.Pcd.increasePcdPrefix(LotConstants.DEFAULT_IGNORE, currentDBMaxLotSeq, length, prefix, ignorePrefixSetting);
            Assert.isFalse(SystemConstant.Pcd.PCD_ID_OUT_OF_RANGE.equals(createdLotSeq),
                           Errors.create().key(MessageIdList.PCD_ID_IS_NOT_ENOUGH).content("PCD创建数量超过PCD规则定义,请联系管理员!").build());
            return createdLotSeq;
        }

        private void updatePodAndDoorStatus(PcdAssembly pcdAssembly, long transRrn, String status) {
            String user = LocalContext.getUserId();
            if (pcdAssembly.getPodRrn() != null && pcdAssembly.getPodRrn() > 0) {
                statusManager.updatePodStatus(pcdAssembly.getPodRrn(), status, user, "", transRrn, "");
            }
            if (pcdAssembly.getDoorRrn() != null && pcdAssembly.getDoorRrn() > 0) {
                statusManager.updateDoorStatus(pcdAssembly.getDoorRrn(), status, user, "", transRrn, "");
            }

        }

        private String getFromCarriertypeLocation(Lot lot) {
            String processLocation = getCarriertypeByProcessLocation(lot);

            Matcher mathcher = PCDPATERN.matcher(processLocation);

            Boolean match = mathcher.matches();
            if (match) {
                processLocation = processLocation.substring(0, processLocation.indexOf(CSTTYPE_SEPERATER_KEY));
            }

            return processLocation;
        }

        private String getToCarrierTypeLocation(Lot lot) {
            String processLocation = getCarriertypeByProcessLocation(lot);

            Matcher mathcher = PCDPATERN.matcher(processLocation);

            Boolean match = mathcher.matches();
            if (match) {
                processLocation = processLocation.substring(processLocation.indexOf(CSTTYPE_SEPERATER_KEY) + 1);
            }

            return processLocation;
        }

        private void createLotTransHistoryForExchanegCarrier(long transRrn, long sequence, Lot lot) {
            Lot lotExt = lotQueryManager.qryLotExtInfo(lot.getLotRrn());
            lot.setCustomerId(StringUtils.isNotBlank(lotExt.getCustomerId()) ? lotExt.getCustomerId() : "");
            lot.setShippingCode(StringUtils.isNotBlank(lotExt.getShippingCode()) ? lotExt.getShippingCode() : "");
            lot.setOuterOrderNO(StringUtils.isNotBlank(lotExt.getOuterOrderNO()) ? lotExt.getOuterOrderNO() : "");
            lot.setOutOrderType(StringUtils.isNotBlank(lotExt.getOutOrderType()) ? lotExt.getOutOrderType() : "");
            if (!lot.getSorterFlag()) {
                lot.setLocation(StringUtils.isNotBlank(lotExt.getLocation()) ? lotExt.getLocation() : "");
            }
            lot.setpLocation(StringUtils.isNotBlank(lotExt.getpLocation()) ? lotExt.getpLocation() : "");
            lotManager.insertLotTransH(transRrn, sequence, lot);
        }

        private void updateCarrier(Long transRrn, String pollutionLevel, Long newCarrierRrn, Long carrierMappingRrn,
                                   String lotId, String carrierId, String status) {
            carrierDAO.updateCarrierMapRrnAndPollutionLevel(newCarrierRrn, carrierMappingRrn, pollutionLevel, status);
            carrierDAO.insertCarrierH(newCarrierRrn, StringUtils.EMPTY, carrierId,
                                      new TransactionLog(transRrn, LocalContext.getUserRrn()));
            PcdClean cleanInfo = pcdManager.getPcdCleanInfo(newCarrierRrn);
            if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
                pcdManager.insertPcdCleanH(transRrn, cleanInfo);
            }
        }

        private void deleteCarrierMapping(Long transRrn, Long carrierMappingRrn) {
            // dao.insertCarrierMappingHistory(transRrn, carrierRrn);
            insertCarrierMappingH(transRrn, carrierMappingRrn);
            // dao.deleteCarrierMapping(carrierRrn);
        }

        private void updateCarrierStatus(TransactionLog transactionLog, long instanceRrn, String targetCarrier,
                                         String targetStatus) {
            carrierDAO.updateCarrierForStatus(instanceRrn, targetStatus);
            carrierDAO.insertCarrierH(instanceRrn, StringUtils.EMPTY, targetCarrier, transactionLog);
            PcdClean cleanInfo = pcdManager.getPcdCleanInfo(instanceRrn);
            if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
                pcdManager.insertPcdCleanH(transactionLog.getTransRrn(), cleanInfo);
            }
        }

        /**
         * 检查Carrier是否被批次使用
         *
         * @param carrierId
         * @param carrierRrn
         */
        private void checkIsInUseCarrier(String carrierId, long carrierRrn) {
            List<String> lots = getInUseCarrierLotIdList(carrierRrn);
            if (!lots.isEmpty()) {
                StringBuilder errorMessage = new StringBuilder();
                errorMessage.append("Cassette Id: ").append(carrierId);
                errorMessage.append(" is already in use by Lot Id: ");
                for (String string : lots) {
                    errorMessage.append(string);
                    errorMessage.append(" ");
                }
                throw new SystemIllegalArgumentException(Errors.create().content(errorMessage.toString()).build());
            }
        }

    }