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