SelectEntityAction.java

package com.mycim.webapp.actions.operation.query;

import com.fa.sesa.threadlocal.LocalContext;
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.valueobject.ems.Entity;
import com.mycim.valueobject.ems.EntityGroup;
import com.mycim.valueobject.ems.Equipment;
import com.mycim.valueobject.security.Station;
import com.mycim.valueobject.wip.LotStatus;
import com.mycim.webapp.actions.WipSetupAction;
import com.mycim.webapp.actions.operation.query.threadtask.EqptBuildTask;
import com.mycim.webapp.actions.operation.query.threadtask.EqptGroupBuildTask;
import com.mycim.webapp.actions.operation.query.threadtask.StationTreeBuildTask;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @author Johnson.Wang
 * @version 6.0.0
 * @date 2019/9/24
 **/
public class SelectEntityAction extends WipSetupAction {

    /**
     * > Station->Entity Group -> Entity->Chamber
     *     问题主要出现在 $EQPT_OWNER 通用标签,且STATION 下 ENTITY_GROUP RRN 为0。
     *     行数据并行处理后会于parent失联,或parent找子会极其困难,且每个station下都会存在,则放弃,
     * 三级并行,目前只有三级。
     *     【若因业务变更出现更多级,则需要修改为递归或其他方式,树根为每个STATION,如因效率问题可使用分段锁】。
     *     先创建任务,在并行处理,之后通过*ptr回调方式处理。之后像'链表'形式关联
     * Note:如果需要单独修改各个属性,直接去在对应task中增加,最好不要对最终结果进行二次修改。
     * @param form
     * @return TreeJSON
     */
    public List<Map<String, Object>>select(Map form) {
        final String filter = MapUtils.getString(form, "filter","ALL");
        final Long userRrn = LocalContext.getUserRrn();
        final String nameSpace =this.getNamedSpace("ENTITY", LocalContext.getFacilityRrn());
        //*ptr
        Supplier<List<Station>> queryStationFunc =()->wipQueryService.getEntities4ExtByUser
        (userRrn,nameSpace,filter);
        //*ptr
        Supplier<Map<String,Integer>>queryContMapFunc =()-> wipQueryService.countLotByStausForEntity(LotStatus.WAITING);
        //双路并行
        List<Object> result = Arrays.asList(queryStationFunc,queryContMapFunc).parallelStream().
                map(e->e.get()).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(result)||result.size()<2){
            return null;
        }
        List<Station> stations=new ArrayList<>();
        Map<String, Integer> countMap=new HashMap<>();
        for(int i =0;i<result.size();i++){
            switch (i){
                case 0:stations.addAll((List<Station>) result.get(i));break;
                case 1:countMap.putAll((Map<String, Integer>) result.get(i));break;
            }
        }
        List<Map<String,Object>> jsonResult =Collections.synchronizedList(new ArrayList<>());
        //创建任务
        createParallelTaskAndExecute(stations,filter,jsonResult,countMap);
        //并行处理完成后 乱序 需要按照 名字进行升序排序
        sortByAsc(jsonResult);
        return jsonResult;
    }


    private void sortByAsc(List<Map<String,Object>> jsonResult){
        //老代码非标准树结构 排序比较费劲,排序对比字段不同
        jsonResult.sort(Comparator.comparing(a -> MapUtils.getString(a, "text", "")));
        for(Map<String,Object> item :jsonResult){
            Object obj = MapUtils.getObject(item, "children");
            if (!(obj instanceof List)) {
                continue;
            }
            List<Map<String,Object>>children = (ArrayList) obj;
            if(CollectionUtils.isEmpty(children)){
                continue;
            }
            sortByAsc(children);//递归
        }
    }


    /**
     * 内部暂无检查非空 仅限于 SelectEntityAction 调用
     * 入参做好 NPE等判断
     * @param stations
     * @param filter
     * @param jsonResult
     * @param countMap
     */
    private void createParallelTaskAndExecute(List<Station> stations,
                                              String filter,List<Map<String,Object>>jsonResult,Map<String,Integer> countMap){

        //key: station id + entity group id
        ConcurrentHashMap<String,Map<String,Object>>eqptGroupResultMap= new ConcurrentHashMap<>();
        //key: station id + entity group id + entity id
        ConcurrentHashMap<String,Map<String,Object>> entityResultMap=new ConcurrentHashMap<>();
        List<StationTreeBuildTask> stationTreeBuildTasks=new ArrayList<>();
        List<EqptGroupBuildTask> eqptGroupBuildTasks =new ArrayList<>();
        List<EqptBuildTask> eqptBuildTasks=new ArrayList<>();
        final Set<Long> eqptEntityRrns =new HashSet<>();
        //任务拆分
        for(int i=0;i<stations.size();i++){
            Station station = stations.get(i);
            //创建station 任务 也许变量i 应该传入 后面排序可能有用
            stationTreeBuildTasks.add(new StationTreeBuildTask(station, jsonResult));
            //station 创建 entity group 任务
            List<EntityGroup> entityGroups = filterEntityGroupByStations(Arrays.asList(station));
            for(EntityGroup entityGroup: entityGroups){
                //创建eqptgroup 任务
                eqptGroupBuildTasks.add(new EqptGroupBuildTask(entityGroup, station, filter, eqptGroupResultMap));
                //根据eqpt group 创建 chamber 任务
                List<Entity>  entities = filterEntitiesByEntityGroup(Arrays.asList(entityGroup));
                if(CollectionUtils.isNotEmpty(entities)){
                    //创建 entity 任务
                    for(Entity entity : entities){
                        if(entity==null){
                            continue;
                        }
                        eqptEntityRrns.add(entity.getInstanceRrn());
                        eqptBuildTasks.add(new EqptBuildTask(filter, countMap, entity, station, entityGroup, wipQueryService,
                                                             entityResultMap));
                    }
                }
            }
        }
        //key parent eqpt rrn
        //value eqpt rows
        Map<Long,List<Equipment>> eqptsChambers  =new HashMap<>();
        Runnable eqptChamberFunc =()->{
            eqptsChambers.putAll(emsService.batchQueryChambersByParentRrn(eqptEntityRrns));
        };
        Arrays.asList(
                eqptGroupBuildTasks,
                eqptBuildTasks,
                stationTreeBuildTasks,
                Arrays.asList(eqptChamberFunc)
        ).parallelStream().forEach(e->e.parallelStream().forEach(Runnable::run));
        //entity group 填充 eqpt
        eqptGroupResultMap.entrySet().parallelStream().forEach(e->{
            Map <String,Object> item =  e.getValue();//null 由容器保证
            Object callBackFunc =  item.get("callback");
            if(callBackFunc!=null&&callBackFunc instanceof BiConsumer){
                ((BiConsumer) callBackFunc).accept(entityResultMap,item);
            }
            if(!item.containsKey("children")){
                eqptGroupResultMap.remove(e.getKey());
            }
        });
        //entity 填充chamber
        entityResultMap.values().parallelStream().forEach(e->{
            Object callBackFunc =  e.get("callback");
            if(callBackFunc!=null&&callBackFunc instanceof BiConsumer){
                ((BiConsumer) callBackFunc).accept(eqptsChambers,e);
            }
        });
        jsonResult.parallelStream().forEach(e->{
            Object callBackFunc =  e.get("callback");
            if(callBackFunc!=null&&callBackFunc instanceof BiConsumer){
                ((BiConsumer) callBackFunc).accept(eqptGroupResultMap,e);
            }
        });
        for(int i = jsonResult.size()-1;i>=0;i--){//删除空节点
            Map<String,Object> item = jsonResult.get(i);
            Object children;
            if((children= item.get("children"))==null){
                //remove self
                jsonResult.remove(i);
                continue;
            }
            if(CollectionUtils.isEmpty(((List)children))){
                jsonResult.remove(i);
            }
        }
    }

    /**
     * 得到 List<station> 所有的 Entity Groups去重复后返回
     *
     * @param stations
     * @return
     */
    private List<EntityGroup> filterEntityGroupByStations(List<Station> stations) {
        if (CollectionUtils.isEmpty(stations)) {
            return new ArrayList<>();//代码混乱防止NPE
        }
        List<Object> tempEntityCollection = stations.stream().map(Station::getEntityGroups)
                                                        .collect(ArrayList::new,ArrayList::addAll,ArrayList::addAll);
        List<EntityGroup> entityGroups = new ArrayList<>();
        for (Object obj : tempEntityCollection) {//station entity group Collection 内无范性
            if (obj instanceof EntityGroup) {//这里安全处理
                //没有到类中重写equals 方法
                //判断是否重复后才进行添加
                boolean contains = entityGroups.stream().anyMatch(
                        e -> StringUtils.equals(e.getInstanceId(), ((EntityGroup) obj).getInstanceId()));
                if (!contains) {
                    entityGroups.add((EntityGroup) obj);
                }
            }
        }
        return entityGroups;
    }

    private List<Entity> filterEntitiesByEntityGroup(List<EntityGroup> entityGroups) {
        if (CollectionUtils.isEmpty(entityGroups)) {
            return new ArrayList<>();//代码混乱防止NPE
        }
        List<Object> tempEntityCollection = entityGroups.stream().map(EntityGroup::getEntities)
                                                    .collect(ArrayList::new,ArrayList::addAll,ArrayList::addAll);
        List<Entity> entities = new ArrayList<>();
        for (Object obj : tempEntityCollection) {// entity group entities 内无范性
            if (obj instanceof Entity) {//这里安全处理
                //为了更少的侵入
                //没有到类中重写equals 方法
                //判断是否重复后才进行添加
                boolean contains = entityGroups.stream().anyMatch(
                        e -> Long.compare(e.getInstanceRrn(), ((Entity) obj).getInstanceRrn())==0);
                if (!contains) {
                    entities.add(((Entity) obj));
                }
            }
        }
        return entities;
    }

}