ManualAndSortCheckAction.java

package com.mycim.webapp.actions.lot;

import com.fa.sesa.exception.Assert;
import com.fa.sesa.exception.Errors;
import com.fa.sesa.exception.TransformFunc;
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.framework.utils.msg.JsonUtils;
import com.mycim.valueobject.LocationNames;
import com.mycim.valueobject.MessageIdList;
import com.mycim.valueobject.ObjectList;
import com.mycim.valueobject.consts.SorterEnum;
import com.mycim.valueobject.ems.Carrier;
import com.mycim.valueobject.prp.Item;
import com.mycim.valueobject.runcard.util.RunCardStoreSubStatus;
import com.mycim.valueobject.sorter.SorterDetailBean;
import com.mycim.valueobject.wip.*;
import com.mycim.webapp.actions.WipSetupAction;

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

/**
 * Manual 和 Sort 的一些共用的校验
 *
 * @Author: yibing.liu
 * @Date: 2021/8/2 11:45
 */
public class ManualAndSortCheckAction extends WipSetupAction {

    protected void checkMergeAvailable(Lot sourceLot, Lot targetLot) {
        Boolean checkResult = false;
        checkResult = !StringUtils.equals(sourceLot.getLotType(), targetLot.getLotType());
        Assert.isFalse(checkResult, Errors.create().key(MessageIdList.LOT_CANT_MERGE)
                                          .content("The lot's {} are different,can't merge!").args("lotType").build());
    }

    protected boolean checkMergeBeforeUpdateDB(HashMap valueMap) {
        Lot tempLot = (Lot) valueMap.get("lot");
        Lot lot = lotQueryService.getLot(tempLot.getLotRrn());
        List childLots = (List) valueMap.get("childLots");
        List unitsOfParentLot = (List) valueMap.get("unitOfParent");
        List newUnits = (List) valueMap.get("newUnits");

        List pLotUnits = wipQueryService.getUnitListByLot(lot.getLotRrn());
        // 检查母批qty1 与 unit的真实数量 是否一致
        if (pLotUnits.size() != unitsOfParentLot.size() || lot.getQty1() != pLotUnits.size()) {
            return true;
        }
        for (Object childLot : childLots) {
            Map clot = (Map) childLot;
            Lot tempChildLot = lotQueryService.getLot(MapUtils.getString(clot, "childLotId"), tempLot.getFacilityRrn());
            List clotUnits = wipQueryService.getUnitListByLot(tempChildLot.getLotRrn());
            if (clotUnits.size() < 1) {
                return true;
            }
            // 检查子批qty1 与 unit的真实数量 是否一致
            if (tempChildLot.getQty1() != clotUnits.size()) {
                return true;
            }
            pLotUnits.addAll(clotUnits);

        }
        // 检查合批后的unit数量是否 等于母批+子批Unit数量之和
        if (pLotUnits.size() != newUnits.size()) {
            return true;
        }
        // 1.检查子批,母批片号是否与数据库一致 ;
        // 2.检查合批位置是否正确
        for (Object pLotUnit : pLotUnits) {
            Map dbUnit = (Map) pLotUnit;
            Long dbUnitRrn = MapUtils.getLong(dbUnit, "unitRrn");
            String dbUnitId = MapUtils.getString(dbUnit, "unitId");
            boolean isHasUnit = false;
            for (Object unit : newUnits) {
                Map newUnit = (Map) unit;
                Long newUnitRrn = MapUtils.getLong(newUnit, "unitRrn");
                String newUnitId = MapUtils.getString(newUnit, "unitId");
                if (dbUnitRrn.longValue() == newUnitRrn.longValue() && StringUtils.equals(dbUnitId, newUnitId)) {
                    isHasUnit = true;
                }
            }
            // 没有匹配到相应的unit
            if (!isHasUnit) {
                return true;
            }
        }
        return false;
    }

    protected void lotBeforeIntoServiceCheck(Lot tempLot, String type) {
        Lot lot = lotQueryService.getLot(tempLot.getLotRrn());
        char[] options = type.trim().toUpperCase().toCharArray();
        for (char option : options) {
            if ('S' == option) {
                Assert.isTrue(StringUtils.equalsIgnoreCase(tempLot.getLotStatus(), lot.getLotStatus()),
                              Errors.create().content("The status of LOT has changed! Please enter F5 refresh!")
                                    .build());
            } else if ('O' == option) {
                Assert.isFalse(tempLot.getOperationRrn().longValue() != lot.getOperationRrn().longValue(),
                               Errors.create().content("The Step of LOT has changed! Please enter F5 refresh!")
                                     .build());
            } else if ('Q' == option) {
                Assert.isFalse(tempLot.getQty1().doubleValue() != lot.getQty1().doubleValue(),
                               Errors.create().content("The QTY of LOT has changed! Please enter F5 refresh!").build());
            }
        }
    }

    protected void childLotsBeforeIntoServiceCheck(List childLots) {
        String childLotId;
        for (Object childLot : childLots) {
            childLotId = MapUtils.getString((Map) childLot, "childLotId");
            Lot childLotInfo = lotQueryService.getLot(childLotId);
            checkChildLotStatusForMerge(childLotInfo);
        }
    }

    protected void checkChildLotStatusForMerge(Lot childLot) {
        Assert.isTrue(LotStatus.isHold(childLot.getLotStatus()),
                      Errors.create().key(MessageIdList.LOT_CANT_MERGE).content("The childLot status must be in HOLD! Please check!").build());
    }


    /**
     * 校验流程版本是否修改
     *
     * @param lot      母批
     * @param childLot 子批
     */
    protected void checkProductVersionForMerge(Lot lot, Lot childLot) {
        boolean productFlag = !StringUtils.equalsIgnoreCase(lot.getProductId(), childLot.getProductId());
        boolean productVersionFlag = !lot.getProductVersion().equals(childLot.getProductVersion());
        Assert.isFalse(productFlag || productVersionFlag,
                       Errors.create().content("ParentLot and ChildLot products or product versions are different!")
                             .build());
    }

    /**
     * 校验产品版本是否修改
     *
     * @param lot      母批
     * @param childLot 子批
     */
    protected void checkProcessVersionForMerge(Lot lot, Lot childLot) {
        boolean processFlag = !StringUtils.equalsIgnoreCase(lot.getProcessId(), childLot.getProcessId());
        boolean processVersionFlag = !lot.getProcessVersion().equals(childLot.getProcessVersion());
        Assert.isFalse(processFlag || processVersionFlag,
                       Errors.create().content("ParentLot and ChildLot processes or process versions are different!")
                             .build());
    }

    /**
     * @param carrierRrn
     * @return boolean
     * @Description 判断carrier是否是空晶舟
     * @author Aiden
     */
    protected boolean carrierIsEmpty(Long carrierRrn) {
        List<Map> units = wipQueryService.getUnitListByCarrier(carrierRrn);
        return units == null || units.isEmpty();
    }

    protected String initEmptyCarrier(int slotCount) {
        List<Map<String, Object>> emptyUnits = new ArrayList<>();
        for (int i = 1; i <= slotCount; i++) {
            Map<String, Object> unitMap = new HashMap<>();
            unitMap.put("position", i + "");
            unitMap.put("unitId", "");
            unitMap.put("unitRrn", "");
            unitMap.put("lotId", "");
            emptyUnits.add(unitMap);

        }

        return JsonUtils.toString(emptyUnits);
    }

    protected void checkLotForSplit(Lot lot, String splitType) {
        // 永久分批flag初始化提示
        Assert.isFalse((lot == null) || (lot.getLotRrn() <= 0),
                       Errors.create().key(MessageIdList.SCRAPLOT_LOTID_ERROR).content("Lot id not find!").build());
        // Bonding临时分批卡控
        Assert.isFalse(isSapphireLot(lot) && LocationNames.TEMPORARY.equalsIgnoreCase(splitType),
                       Errors.create().key(MessageIdList.BOND_LOT_CAN_NOT_SPLIT).build());
        // check the lot status by product typess
        /*if (!lot.getLotStatus().equalsIgnoreCase(LotStatus.HOLD)) {
            throw new WebException("lot.spilt_not_hold", "This lot is not in hold");
        }*/

        Assert.isTrue(LotStatus.isWaitingOrHold(lot.getLotStatus()),
                      Errors.create().key(MessageIdList.LOT_MUST_W_H).build());

        Assert.isFalse(diffBatchQueryService.checkLotInBatch(lot.getLotRrn()),
                       Errors.create().key(MessageIdList.LOT_IN_BATCH).content("Lot in batch!").build());

        wipQueryService.checkActiveInlineOcapId(lot.getLotRrn());

        wipCheckService.checkPiLotNormalFunction(lot.getLotRrn(), lot.getBasedLotRrn());
    }

    protected boolean isSapphireLot(Lot lot) {
        Item item = new Item(lot.getProductRrn());
        item = prpService.getItem(item);
        // 如果是Bonding类型
        return StringUtils.equalsIgnoreCase(item.getObjectType(), ObjectList.SAPPHIRE);
    }

    /**
     * child lot和lot必须都bond或都未bond才允许合批
     *
     * @param parentLot
     * @param childLot
     */
    protected void checkBond(Lot parentLot, Lot childLot) {
        //#44487 将Check降到Wafer级别 卡控是否做了bond操作
        Boolean parentLotBondingFlag = false;
        Boolean childLotBondingFlag = false;
        List<Map> parentLotUnitList = wipQueryService.getUnitListByCarrier(parentLot.getCarrierRrn());
        List<Map> childLotLotUnitList = wipQueryService.getUnitListByCarrier(childLot.getCarrierRrn());
        if(CollectionUtils.isNotEmpty(parentLotUnitList)){
            parentLotBondingFlag = lotQueryService.getUnitBondingInfo(parentLotUnitList);
        }
        if(CollectionUtils.isNotEmpty(childLotLotUnitList)){
            childLotBondingFlag = lotQueryService.getUnitBondingInfo(childLotLotUnitList);
        }
        Assert.isTrue((parentLotBondingFlag && childLotBondingFlag) ||
                              (!parentLotBondingFlag && !childLotBondingFlag),
                      Errors.create().key(MessageIdList.LOT_IS_BONDING).content("child lot is bonding Not allowed to merge!")
                            .build());

        //如果是分批之后再bond的情况 此时的childLot有bond信息
        List<Bonding> childBondInfo = lotQueryService.getBondingsByLotRrn(childLot.getLotRrn(), StringUtils.EMPTY);
        if (CollectionUtils.isNotEmpty(childBondInfo) && parentLotBondingFlag && childLotBondingFlag) {
            //母子批的bond辅产品必须同源
            Assert.isTrue(wipCheckService.checkParentAndChildSourceMerge(parentLot, childLot),
                          Errors.create().key(MessageIdList.BOND_MERGE_SOURCE).content("DeBond steps exist and bond lots come from different sources!").build());
        }
    }

    /**
     * child and parent lot need the same contamination level
     *
     * @param parentLot
     * @param childLot
     */
    protected void checkContaminationLevel(Lot parentLot, Lot childLot) {
        String parentContaminationLevelStr = parentLot.getPollutionLevel();
        String childContaminationLevelStr = childLot.getPollutionLevel();
        if (StringUtils.isNotBlank(parentContaminationLevelStr) && StringUtils.isNotBlank(childContaminationLevelStr)) {
            int parentContaminationLevel = NumberUtils.toInt(parentContaminationLevelStr, -1);
            int childContaminationLevel = NumberUtils.toInt(childContaminationLevelStr, -1);
            if (parentContaminationLevel >= 0 && childContaminationLevel >= 0) {
                Assert.state(parentContaminationLevel == childContaminationLevel,
                             Errors.create().key(MessageIdList.CONTAIMNATION_SHOULD_BE_CONSISTENT).content(
                                     "The contamination level of the child lot and the parent lot must be " +
                                             "consistent!").build());
            }
        }
    }

    /**
     * Check flip for childLot and parentLot
     * @param parentLot 父批
     * @param childLot  子批
     */
    protected void checkFlip4ChildAndParentLot(Lot parentLot, Lot childLot) {
        Assert.isFalse(!StringUtils.equalsIgnoreCase(parentLot.getFlipType(), childLot.getFlipType()),
                       Errors.create().key(MessageIdList.CHILD_PARENT_NOT_SAME)
                             .content("The Flip Type of the child Lot is inconsistent with that of the parent Lot!").build());
    }

    protected void checkFlip4ChildAndParentLot(String parentFlipType, String childFlipType){
        Assert.isFalse(!StringUtils.equalsIgnoreCase(parentFlipType, childFlipType),
                       Errors.create().key(MessageIdList.CHILD_PARENT_NOT_SAME)
                             .content("The Flip Type of the child Lot is inconsistent with that of the parent Lot!").build());
    }

    protected String[] buildChildAndParentLotId4RunCard(long lotRrn, String lotId){
        String[] lotIds = new String[2];
        LotRunCardStore lotStore = lotRunCardQueryService.getSplitRunCardLotStore(lotRrn);
        List<Map<String, Object>> subList = lotRunCardQueryService.getRunCardLotsByRuncardRrn(lotStore.getRuncardRrn());

        List<Map<String, Object>> finishLots = new ArrayList<>();
        for (Map<String, Object> map : subList) {
            if (StringUtils
                    .equalsIgnoreCase(RunCardStoreSubStatus.FINISH.toString(), MapUtils.getString(map, "subStatus"))) {
                finishLots.add(map);
            }
        }

        Assert.isFalse(CollectionUtils.isEmpty(finishLots), Errors.create().key(MessageIdList.CAN_NOT_MERGE).content("Can't merge lot").build());

        String parentLotId = lotId;
        String childLotId = null;

        for (Map<String, Object> finishLot : finishLots) {
            String finishLotId = MapUtils.getString(finishLot, "lotId");

            if (StringUtils.equals(MapUtils.getString(finishLot, "lotId"), lotId)) {
                continue;
            }

            if (MapUtils.getLongValue(finishLot, "lotRrn") == MapUtils.getLongValue(finishLot, "baseLotRrn")) {
                childLotId = parentLotId;
                parentLotId = finishLotId;
                break;
            } else {
                if (!StringUtils.equals(MapUtils.getString(finishLot, "lotId"), lotId)) {
                    childLotId = finishLotId;
                }
            }
        }

        lotIds[0] = childLotId;
        lotIds[1] = parentLotId;
        return lotIds;
    }

    protected void checkQTime(long parentLotRrn, long childLotRrn) {
        String qtimeErrorMsg = buidldQtimeErrorMsg(parentLotRrn, childLotRrn);
        Assert.isFalse(qtimeErrorMsg.length() > 0, Errors.create().content(qtimeErrorMsg).build());
    }

    protected String buidldQtimeErrorMsg(Long paraLotRrn, Long childLotRrn) {
        List<TimelimitStatus> paraQTimes = lotQueryService.getLotTimeLimitStatusByLotRrn(paraLotRrn, StringUtils.EMPTY);
        List<TimelimitStatus> childQTimes = lotQueryService.getLotTimeLimitStatusByLotRrn(childLotRrn, StringUtils.EMPTY);

        StringBuilder errorMsg = new StringBuilder();

        Boolean existFlag;
        for (TimelimitStatus paraQtime : paraQTimes) {
            existFlag = false;
            for (TimelimitStatus childQtime : childQTimes) {
                if (StringUtils.equalsIgnoreCase(paraQtime.getUniqueKeyWithOutStatus(),
                                                 childQtime.getUniqueKeyWithOutStatus())) {
                    existFlag = true;
                    break;
                }
            }

            if (!existFlag) {
                errorMsg.append(paraQtime.getTimeLimitId()).append(",");
            }

        }

        for (TimelimitStatus childQtime : childQTimes) {
            existFlag = false;
            for (TimelimitStatus paraQtime : paraQTimes) {
                if (StringUtils.equalsIgnoreCase(paraQtime.getUniqueKeyWithOutStatus(),
                                                 childQtime.getUniqueKeyWithOutStatus())) {
                    existFlag = true;
                    break;
                }
            }

            if (!existFlag) {
                errorMsg.append(childQtime.getTimeLimitId()).append(",");
            }

        }

        if (errorMsg.length() > 0) {
            errorMsg.deleteCharAt(errorMsg.length() - 1);
            errorMsg.insert(0, "Following timelimits not match:</br>");
        }
        if (errorMsg.length() > 0) {
            errorMsg.append("</br>");
        }
        return errorMsg.toString();

    }


    protected List<SorterDetailBean> parseSorterDetailBeanList(String jsonStr) {
        if (StringUtils.isBlank(jsonStr)){
            return new ArrayList<>();
        }
        List list = JsonUtils.toObject(jsonStr, List.class);
        return parseSorterDetailBeanList((List<Map>) list);
    }

    protected List<SorterDetailBean> parseSorterDetailBeanList(List<Map> unitMapList) {
        if (CollectionUtils.isEmpty(unitMapList)){
            return new ArrayList<>();
        }
        List<SorterDetailBean> sortJobDetailList = new ArrayList<>();
        for (Map unitMap : unitMapList) {
            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);
            if (StringUtils.isNotBlank(unitId) && unitRrn > 0
                    && StringUtils.isNotBlank(lotId) && (position > 0 && position < 26)) {
                sortJobDetailList.add(new SorterDetailBean(lotRrn, lotId, unitRrn, unitId, position, position));
            }
        }
        return sortJobDetailList;
    }

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


    protected List<Map> parseUnits(List<Unit> unitList) {
        List<Map> unitMapList = new ArrayList<>();
        for (Unit u:unitList){
            Map unit = new HashMap<>();
            unit.put("lotRrn", u.getLotRrn());
            unit.put("unitRrn", u.getUnitRrn());
            unit.put("unitId", u.getUnitId());
            unit.put("position", u.getPositionInCarrier());
            unit.put("unitstatus", u.getUnitStatus());
            unit.put("lotid", u.getLotId());
            unit.put("lotId", u.getLotId());
            unit.put("t7code", u.getT7code());
            unit.put("ppid", u.getRecipeId());
            unit.put("recipeId", u.getRecipeId());
            unit.put("unitAlias1", u.getUnitAlias1());
            unit.put("unitAlias2", u.getUnitAlias2());
            unit.put("itemId", u.getItemId());
            unitMapList.add(unit);
        }
        return unitMapList;
    }

    protected List<BiFunction<Carrier, Carrier, TransformFunc>> buildCarrierCheckFunction(String type, long facilityRrn) {
        final List<BiFunction<Carrier, Carrier, TransformFunc>> functionList = new ArrayList<>();
        //检查源晶舟和目标晶舟是否为DUMMY晶舟
        BiFunction<Carrier, Carrier, TransformFunc> checkDummy = (sc, tc) -> {
            if ((SortJob.ObjectType.DUMMY.toString().equals(sc.getObjectSubtype()) || SortJob.ObjectType.DUMMY.toString().equals(tc.getObjectSubtype())))
                return Errors.create().key(MessageIdList.CANNOT_USE_DUMMY_CARRIER).build();
            return null;
        };
        //检查源晶舟和目标的类型是否一致
        final BiFunction<Carrier, Carrier, TransformFunc> checkCateGory = (sc, tc) -> {
            if (tc.getInstanceRrn() > 0 && !StringUtils.equals(sc.getFlagType(), tc.getFlagType()))
                return Errors.create().key(MessageIdList.CATEGORY_NOT_SAME).build();
            return null;
        };
        //检查源晶舟和目标晶舟是否是同一类型
        final BiFunction<Carrier, Carrier, TransformFunc> checkCarrierIsSameType = (sc, tc) -> {
            if (tc.getInstanceRrn() > 0 && !StringUtils.equals(sc.getObjectSubtype(), tc.getObjectSubtype()))
                return Errors.create().key(MessageIdList.CARRIER_TYPE_NOT_SAME).build();
            return null;
        };
        //检查源晶舟和目标晶舟的是否需要组合
        final BiFunction<Carrier, Carrier, TransformFunc> checkCarrierStatus = (sc, tc) -> {
            // if (sc != null && sc.getInstanceRrn() > 0) sorterService.checkCarrierStatus(sc);
            if (tc != null && tc.getInstanceRrn() > 0)
                sorterService.checkCarrierStatus(tc);
            return null;
        };
        //检查源晶舟和目标晶舟是否是同一个
        final BiFunction<Carrier, Carrier, TransformFunc> checkCarrierIsSame = (sc, tc) -> {
            if (StringUtils.equalsIgnoreCase(sc.getInstanceId(), tc.getInstanceId()))
                return Errors.create().key(MessageIdList.CARRIER_CAN_NOT_SAME).build();
            return null;
        };
        //检查源晶舟和目标晶舟是否被SortJob占用
        final BiFunction<Carrier, Carrier, TransformFunc> checkWaitSortJob = (sc, tc) -> {
            checkWaitJobs(sc.getInstanceRrn(), tc.getInstanceRrn(), 0L, null);
            return null;
        };
        //检查目标晶舟是否需要清洗
        final BiFunction<Carrier, Carrier, TransformFunc> checkTargetCarrierIsValid = (sc, tc) -> {
            if (tc != null && tc.getInstanceRrn() > 0)
                carrierService.checkPcdIsValid(tc.getInstanceRrn(), facilityRrn);
            return null;
        };
        //检查目标晶舟是否被Monitor占用
        final BiFunction<Carrier, Carrier, TransformFunc> checkMonitorCarrierUsed = (sc, tc) -> {
            if (tc.getInstanceRrn() > 0 && lotAutoMonitorInqService.checkMonitorCarrierUsed(tc.getInstanceRrn()))
                return Errors.create().key(MessageIdList.AUTOMONITOR_CARRIER_INUSED).build();
            return null;
        };

        functionList.add(checkDummy);
        functionList.add(checkCarrierIsSame);
        functionList.add(checkCarrierStatus);
        functionList.add(checkWaitSortJob);
        functionList.add(checkTargetCarrierIsValid);
        functionList.add(checkMonitorCarrierUsed);
        if (SorterEnum.Constant.SRC_EXCHANGE.equalsIgnoreCase(type)){
            functionList.add(checkCateGory);
            functionList.add(checkCarrierIsSameType);
        }

        return functionList;
    }
}