xiejun
2023-06-15 694374bb1c6c08e3e79a65f575922e61c5c971fc
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/service/impl/MdmIOServiceImpl.java
@@ -2,12 +2,16 @@
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.vci.ubcs.code.bo.AttributeValue;
import com.vci.ubcs.code.bo.CodeClassifyFullInfoBO;
import com.vci.ubcs.code.bo.CodeTemplateAttrSqlBO;
import com.vci.ubcs.code.dto.CodeOrderDTO;
import com.vci.ubcs.code.entity.CodeAllCode;
import com.vci.ubcs.code.entity.CodeClassify;
import com.vci.ubcs.code.enumpack.CodeDefaultLC;
import com.vci.ubcs.code.enumpack.CodeLevelTypeEnum;
import com.vci.ubcs.code.lifecycle.CodeAllCodeLC;
import com.vci.ubcs.code.mapper.CommonsMapper;
import com.vci.ubcs.code.service.*;
import com.vci.ubcs.code.util.ClientBusinessObject;
@@ -16,6 +20,8 @@
import com.vci.ubcs.code.vo.webserviceModel.attrmap.DataObjectVO;
import com.vci.ubcs.code.vo.webserviceModel.attrmap.RowDatas;
import com.vci.ubcs.code.vo.webserviceModel.result.xml.XMLResultDataObjectDetailDO;
import com.vci.ubcs.omd.feign.IBtmTypeClient;
import com.vci.ubcs.omd.vo.BtmTypeVO;
import com.vci.ubcs.starter.bo.WriteExcelData;
import com.vci.ubcs.starter.exception.VciBaseException;
import com.vci.ubcs.starter.poi.bo.ReadExcelOption;
@@ -26,6 +32,8 @@
import com.vci.ubcs.starter.revision.model.BaseModel;
import com.vci.ubcs.starter.util.DefaultAttrAssimtUtil;
import com.vci.ubcs.starter.util.LocalFileUtil;
import com.vci.ubcs.starter.util.MdmBtmTypeConstant;
import com.vci.ubcs.starter.web.constant.QueryOptionConstant;
import com.vci.ubcs.starter.web.enumpck.BooleanEnum;
import com.vci.ubcs.starter.web.enumpck.VciFieldTypeEnum;
import com.vci.ubcs.starter.web.pagemodel.DataGrid;
@@ -36,6 +44,7 @@
import com.vci.ubcs.starter.web.util.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.map.HashedMap;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
@@ -43,17 +52,17 @@
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Workbook;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.api.R;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;
@@ -66,6 +75,21 @@
@Service
@Slf4j
public class MdmIOServiceImpl implements MdmIOService {
   /**
    * 字段
    */
   public static final String ROW_INDEX = "LAY_TABLE_INDEX";
   /**
    * 错误信息的字段
    */
   public static final String ERROR_MSG = "errorMsg";
   @Value("${batchadd.exportattr.type:基本信息}")
   public String BATCHADD_EXCEPORT_ATTR_TYPE;
   @Value("${batchadd.redis.time:6000000}")
   public int BATCHADD_REDIS_TIME;
   /**
@@ -74,11 +98,17 @@
   @Resource
   private ICodeClassifyService classifyService;
   /**
    * 通用查询
    */
   @Resource
   private CommonsMapper commonsMapper;
   /****
    * 码值服务
    */
   @Resource
   ICodeAllCodeService codeAllCodeService;
   /**
    * 模板的服务
@@ -111,6 +141,178 @@
    */
   @Autowired
   private FormulaServiceImpl formulaService;
   /**
    * 规则的服务
    */
   @Autowired
   private ICodeRuleService ruleService;
   /**
    * 业务类型的服务
    */
   @Autowired
   private IBtmTypeClient btmTypeClient;
   /**
    * 批量申请:选取选中分类下的所有模板关键属性,相似属性,必填属性,写入execl中
    *
    * @param codeClassifyOid 分类的主键
    * @return excel的文件地址
    */
   @Override
   public  String downloadTopImportExcel(String codeClassifyOid){
      List<CodeClassifyTemplateVO> templateVOList=new ArrayList<>();
      VciBaseUtil.alertNotNull("导出模板","导出的配置",codeClassifyOid,"主题库分类的主键");
      CodeClassifyVO codeClassifyVO = classifyService.getObjectByOid(codeClassifyOid);
      templateVOList= templateService.childTemplates(codeClassifyOid);
      List<CodeClassifyVO>  codeClassifyVOS=classifyService.getIdPathToNamePathByParentId(codeClassifyOid,true);
      WriteExcelOption eo = new WriteExcelOption();
      LinkedHashMap<String,CodeClassifyTemplateAttrVO> allFieldToOutNameMap=new LinkedHashMap<>();
      templateVOList.stream().forEach(templateVO -> {
         //组合格式的不导入,
         // 枚举的提供序列的选择
         //时间全部统一为yyyy-MM-dd HH:mm:ss
         //参照的自行输入名称
         //分类注入的不用,都是导入后自动处理的
         //编码,状态等字段不导入
         List<CodeClassifyTemplateAttrVO> templateAttrVOS = templateVO.getAttributes().stream().filter(s ->
            !DEFAULT_ATTR_LIST.contains(s.getId())
               && StringUtils.isBlank(s.getComponentRule())
               && StringUtils.isBlank(s.getClassifyInvokeAttr())
               && (VciBaseUtil.getBoolean(s.getFormDisplayFlag()))
         ).collect(Collectors.toList());
         if(CollectionUtils.isEmpty(templateAttrVOS)){
            throw new VciBaseException("模板没有配置任何【表单显示】为【是】的属性");
         }
         templateAttrVOS.stream().forEach(codetemplateAttr ->{
            String field=codetemplateAttr.getId();
            String name=codetemplateAttr.getName();
            CodeClassifyTemplateAttrVO codeBaseAttributeDTO=new CodeClassifyTemplateAttrVO();
            boolean res=codetemplateAttr.getAttributeGroup().equals(BATCHADD_EXCEPORT_ATTR_TYPE)//基本属性字段显示
               ||(StringUtils.isNotBlank(codetemplateAttr.getKeyAttrFlag())&&Boolean.parseBoolean(codetemplateAttr.getKeyAttrFlag()))//关键属性的存入
               ||(StringUtils.isNotBlank(codetemplateAttr.getSameRepeatAttrFlag())&&Boolean.parseBoolean(codetemplateAttr.getSameRepeatAttrFlag())) //相似属性的存入
               ||(StringUtils.isNotBlank(codetemplateAttr.getRequireFlag())&&Boolean.parseBoolean(codetemplateAttr.getRequireFlag()));
            if(allFieldToOutNameMap.containsKey(name)){//如果存在的话则需要根据具体的去赋值
               codeBaseAttributeDTO=  allFieldToOutNameMap.get(name);
               if(StringUtils.isNotBlank(codetemplateAttr.getKeyAttrFlag())&&Boolean.parseBoolean(codetemplateAttr.getKeyAttrFlag())){
                  codeBaseAttributeDTO.setKeyAttrFlag(codetemplateAttr.getKeyAttrFlag());//属性关键属性
               }
               if(StringUtils.isNotBlank(codetemplateAttr.getRequireFlag())&&Boolean.parseBoolean(codetemplateAttr.getRequireFlag())){
                  codeBaseAttributeDTO.setRequireFlag(codetemplateAttr.getRequireFlag());//属性必填项
               }
               if(StringUtils.isNotBlank(codetemplateAttr.getSameRepeatAttrFlag())&&Boolean.parseBoolean(codetemplateAttr.getSameRepeatAttrFlag())){
                  codeBaseAttributeDTO.setSameRepeatAttrFlag(codetemplateAttr.getSameRepeatAttrFlag());//属性相似属性
               }
            }else if(res){
               allFieldToOutNameMap.put(name,codetemplateAttr);
            }
         });
      });
      //整理好所有模板需要写入execl的属性信息
      Workbook workbook = new HSSFWorkbook();
      LinkedList<WriteExcelData> excelDataList = new LinkedList<>();
      if(!CollectionUtils.isEmpty(allFieldToOutNameMap)){
         excelDataList.add(new WriteExcelData(0,0,"分类路径"));
         final int[] index = {0};
         allFieldToOutNameMap.values().stream().forEach(attrVO -> {
            Object text = attrVO.getName();
            text = exportKeyAndRequired(workbook,attrVO,text);
            int colIndex = 1 + index[0]++;
            WriteExcelData excelData = new WriteExcelData(0, colIndex, text);
            if(StringUtils.isNotBlank(attrVO.getCodeDateFormat())
               || VciFieldTypeEnum.VTDateTime.name().equalsIgnoreCase(attrVO.getAttributeDataType())
               || VciFieldTypeEnum.VTDate.name().equalsIgnoreCase(attrVO.getAttributeDataType())
               ||VciFieldTypeEnum.VTTime.name().equalsIgnoreCase(attrVO.getAttributeDataType())){
               excelData.setDateFormat(VciDateUtil.DateTimeFormat);
            }
            if(text instanceof RichTextString){
               excelData.setFontColor(String.valueOf(HSSFColor.HSSFColorPredefined.RED.getIndex()));
            }
            excelDataList.add(excelData);
            if(StringUtils.isNotBlank(attrVO.getEnumString()) || StringUtils.isNotBlank(attrVO.getEnumId())){
               //添加数据有效性
               List<String> enumValueList = new ArrayList<>();
               enumValueList.add("");
               List<KeyValue> valueList = engineService.listComboboxItems(attrVO);
               if(!CollectionUtils.isEmpty(valueList)){
                  valueList.stream().forEach(kv->{
                     enumValueList.add(kv.getValue());
                  });
               }
               //默认加1万条
               WriteExcelData ed = new WriteExcelData(1,colIndex,"");
               ed.setRowTo(100);
               ed.setColTo(colIndex);
               ed.setValidation(true);
               ed.setValidationDataList(enumValueList);
               ed.setValidationErrorMsg("请在序列中选择正确的值");
               excelDataList.add(ed);
            }
            if(VciFieldTypeEnum.VTBoolean.name().equalsIgnoreCase(attrVO.getAttributeDataType())){
               List<String> booleanList = new ArrayList<>();
               booleanList.add("是");
               booleanList.add("否");
               //默认加1万条
               WriteExcelData ed = new WriteExcelData(1,colIndex,"");
               ed.setRowTo(100);
               ed.setColTo(colIndex);
               ed.setValidation(true);
               ed.setValidationDataList(booleanList);
               ed.setValidationErrorMsg("请在序列中选择正确的值");
               excelDataList.add(ed);
            }
         });
         eo.addSheetDataList(codeClassifyVO.getName()+"导入模板",excelDataList);
      }
      LinkedList<WriteExcelData> classPathList = new LinkedList<>();
      classPathList.add(new WriteExcelData(0,0,"分类层级"));
      WriteExcelData idPathWriteExcelTitle=new WriteExcelData(0,1,"分类ID路径");
      idPathWriteExcelTitle.setWidth(20);
      idPathWriteExcelTitle.setCenter(false);
      classPathList.add(idPathWriteExcelTitle);
      WriteExcelData namePathWriteExcelTitle=new WriteExcelData(0,2,"分类名称路径");
      namePathWriteExcelTitle.setWidth(20);
      namePathWriteExcelTitle.setCenter(false);
      classPathList.add(namePathWriteExcelTitle);
      final int[] rowIndex = {1};
      codeClassifyVOS.stream().forEach(codeClassifyVO1 -> {
         classPathList.add(new WriteExcelData(rowIndex[0],0,codeClassifyVO1.getDataLevel()));
         String idPath=codeClassifyVO1.getIdPath().startsWith("#")?codeClassifyVO1.getIdPath().substring(1):codeClassifyVO1.getIdPath();
         WriteExcelData idPathWriteExcelData=new WriteExcelData(rowIndex[0],1,idPath);
         idPathWriteExcelData.setWidth(30);
         idPathWriteExcelData.setCenter(false);
         classPathList.add(idPathWriteExcelData);
         String namePath=codeClassifyVO1.getNamePath().startsWith("#")?codeClassifyVO1.getNamePath().substring(1):codeClassifyVO1.getNamePath();
         WriteExcelData  namePathWriteExcelData=  new WriteExcelData(rowIndex[0],2,namePath);
         namePathWriteExcelData.setWidth(40);
         namePathWriteExcelData.setCenter(false);
         classPathList.add(namePathWriteExcelData);
         rowIndex[0]++;
      });
      WriteExcelData  excelData=new WriteExcelData();
      excelData.setMerged(true);
      excelData.setRow(1);
      excelData.setRowTo(2);
      excelData.setCol(4);
      excelData.setColTo(9);
      excelData.setCenter(false);
      excelData.setReadOnly(true);
      excelData.setObj("导入数据时,分类路径必须填写叶子节点路径\n(选择叶子节点导入则不需要填写分类路径)");
      excelData.setFontColor(String.valueOf(HSSFColor.HSSFColorPredefined.RED.getIndex()));
      classPathList.add(excelData);
      eo.addSheetDataList(codeClassifyVO.getName()+"分类对照表",classPathList);
      String excelName = LocalFileUtil.getDefaultTempFolder() + File.separator + codeClassifyVO.getName() + "_导入模板.xls";
      // eo.addSheetDataList(templateVOList.size()+"模板信息【请勿删除或移动】",tempEDList);
      ExcelUtil.writeDataToFile(excelName,eo);
      return excelName;
   }
   /**
    * 生成导入的文件
    *
@@ -307,6 +509,627 @@
      return codeImProtRusultVO;
   }
   /***
    * 从顶层批量申请导入方法
    * @param codeClassifyOid 分类的主键
    * @param classifyAttr 分类路径使用的属性
    * @param file excel文件的信息
    * @return
    */
   @Override
   public CodeImProtRusultVO batchTopImportCode(String codeClassifyOid, String classifyAttr, File file) {
      VciBaseUtil.alertNotNull(codeClassifyOid,"分类的主键");
      ReadExcelOption reo = new ReadExcelOption();
      reo.setReadAllSheet(true);
      List<SheetDataSet> sheetDataSetList = ExcelUtil.readDataObjectFromExcel(file,null,reo);
      if(CollectionUtils.isEmpty(sheetDataSetList) || CollectionUtils.isEmpty(sheetDataSetList.get(0).getRowData())
         ||sheetDataSetList.get(0).getRowData().size()<1){
         throw new VciBaseException("没有读取到任何的数据");
      }
      if(sheetDataSetList.size()>LIMIT+1){
         throw new VciBaseException("为了保证系统的稳定性,请一次不要导入超过1万条的数据");
      }
      //先找到每一行的标题,然后根据标题来获取对应的属性
      SheetDataSet dataSet = sheetDataSetList.get(0);
      CodeClassifyFullInfoBO classifyFullInfo = classifyService.getClassifyFullInfo(codeClassifyOid);
      //获取当前模板
      CodeClassifyTemplateVO selectCodeClassifyTemplateVO =engineService.getUsedTemplateByClassifyOid(codeClassifyOid);
      Map<String,List<ColumnVO>> templateColumnVOMap=new HashMap<>();
      createTemplate(selectCodeClassifyTemplateVO,templateColumnVOMap);
      List<CodeClassifyVO> childClassifyVOs = classifyService.listChildrenClassify(codeClassifyOid, true, classifyAttr, true);
      Map<String/**路径**/, CodeClassifyVO> pathMap = Optional.ofNullable(childClassifyVOs).orElseGet(() -> new ArrayList<>()).stream().collect(Collectors.toMap(s -> s.getPath().startsWith("#") ? s.getPath().substring(1) : s.getPath(), t -> t));
      pathMap.put("#current#", classifyFullInfo.getCurrentClassifyVO());
      List<String> titleRowData = dataSet.getColName();
      Map<String, String> errorMap = new ConcurrentHashMap<>();
      //首先将数据以模板的形式分开
      LinkedHashMap<String,List<CodeImprotDataVO>> codeclassifyDataMap=new LinkedHashMap<>();
      List<CodeImprotDataVO> codeClassifyDatas=new ArrayList<>();
      createExeclClassData(dataSet,pathMap,errorMap,codeClassifyDatas);
      //根据模板将数据整合在一起,去校验
      Map<String/**模板oid**/, List<CodeImprotDataVO>/**数据对象**/> templateDatasMap =codeClassifyDatas.stream().collect(Collectors.toMap(CodeImprotDataVO::getTemplateOid,s->{
         List<CodeImprotDataVO> l=new ArrayList<>();
         l.add(s);
         return l;
      },(List<CodeImprotDataVO> s1,List<CodeImprotDataVO> s2)->{
         s1.addAll(s2);
         return s1;
      }));
      String uuid=VciBaseUtil.getPk();
      List<CodeImportTemplateVO> codeImportTemplateVOS=new ArrayList<>();
      Map<String,CodeImportTemplateVO> codeRuleMap=new HashMap<>();
      //相似数据
      // Map<String,String>wpResembleMap=new HashMap<>();
      // List<CodeImprotDataVO> wpCodeImprotDataVOList=new ArrayList<>();
      //按照模板去整理数据
      templateDatasMap.keySet().stream().forEach(templateVOOid->{
         List<CodeImprotDataVO> codeImprotDataVOS= templateDatasMap.get(templateVOOid);
         CodeClassifyTemplateVO templateVO= templateService.getObjectHasAttrByOid(templateVOOid);
         //除去默认的属性.还有只有表单显示的字段才导入
         List<CodeClassifyTemplateAttrVO> attrVOS = templateVO.getAttributes().stream().filter(s ->
            !DEFAULT_ATTR_LIST.contains(s.getId()) && VciBaseUtil.getBoolean(s.getFormDisplayFlag())
         ).collect(Collectors.toList());
         Map<String/**模板属性字段oid**/, String /**模板属性外部名称**/> fieldNameMap =attrVOS.stream().collect(Collectors.toMap(CodeClassifyTemplateAttrVO::getId,s->s.getName()));
         List<ClientBusinessObject> allCboList=new ArrayList<>();
         codeImprotDataVOS.stream().forEach(codeImprotDataVO -> {
            List<ColumnVO>columnVOList =new ArrayList();
            String templateOid=selectCodeClassifyTemplateVO.getOid();
            if(templateColumnVOMap.containsKey(templateOid)){
               columnVOList= columnVOList=templateColumnVOMap.get(templateOid);
            }else{
               createTemplate(templateVO,templateColumnVOMap);
               columnVOList= columnVOList=templateColumnVOMap.get(templateOid);
            }
            String codeRuleOid=codeImprotDataVO.getCodeRuleOid();
            if(!codeRuleMap.containsKey(codeRuleOid)){
               CodeImportTemplateVO codeImportTemplateVO=new CodeImportTemplateVO();
               codeImportTemplateVO.setRoot(false);
               codeImportTemplateVO.setCodeClassifyOid(codeImprotDataVO.getCodeClassifyOid());
               codeImportTemplateVO.setCodeRuleOid(codeImprotDataVO.getCodeRuleOid());
               codeImportTemplateVO.setCodeTemplateOid (codeImprotDataVO.getTemplateOid());
               codeImportTemplateVO.setCodeClassifyVO( codeImprotDataVO.getCodeClassifyVO());
               codeImportTemplateVO.setCodeClassifyTemplateVO( codeImprotDataVO.getCodeClassifyTemplateVO());
               codeImportTemplateVO.setCodeRuleVO(codeImprotDataVO.getCodeRuleVO());
               List<String> colNames=codeImprotDataVO.getColNames();
               codeImportTemplateVO.setCloNamesList(columnVOList);
               codeImportTemplateVOS.add(codeImportTemplateVO);
               codeRuleMap.put(codeRuleOid,codeImportTemplateVO);
            }
            List<ClientBusinessObject> cboList=new ArrayList<>();
            excelToCbo(classifyFullInfo,codeImprotDataVO,cboList,true);
            allCboList.addAll(cboList);
            //往选择的节点里面加数据
            // CodeImprotDataVO wpcodeImprotDataVO=new CodeImprotDataVO();
            //   BeanUtilForVCI.copyPropertiesIgnoreCase(codeImprotDataVO,wpcodeImprotDataVO);
               /* wpcodeImprotDataVO.setCodeClassifyOid(codeClassifyOid);
                wpcodeImprotDataVO.setCodeClassifyVO(classifyFullInfo.getCurrentClassifyVO());
                wpcodeImprotDataVO.setCodeClassifyTemplateVO(selectCodeClassifyTemplateVO);
                wpcodeImprotDataVO.setCodeRuleOid(classifyFullInfo.getCurrentClassifyVO().getCoderuleoid());*/
            // wpCodeImprotDataVOList.add(wpcodeImprotDataVO);//往物品对象里添加
         });
         //都转换完了。需要批量检查
         //如果出错了,我们依然执行有效的数据,无效的数据写回到excel中
         //2.判断必输项。。需要全部的属性,如果是必输,但是表单里面不显示的,只能是分类注入或者组合规则
         batchCheckRequiredAttrOnOrder(templateVO,allCboList,errorMap);
         //3.判断关键属性
         CodeImportResultVO keyResultVO = batchCheckKeyAttrOnOrder(classifyFullInfo, templateVO, allCboList);
         Set<String> selfRepeatRowIndexList = keyResultVO.getSelfRepeatRowIndexList();
         Set<String> keyAttrRepeatRowIndexList = keyResultVO.getKeyAttrRepeatRowIndexList();
         if(!CollectionUtils.isEmpty(selfRepeatRowIndexList)){
            selfRepeatRowIndexList.stream().forEach(rowIndex->{
               errorMap.put(rowIndex,errorMap.getOrDefault(rowIndex,"") + ";在当前处理的数据文件中关键属性重复" );
            });
         }
         if(!CollectionUtils.isEmpty(keyAttrRepeatRowIndexList)){
            keyAttrRepeatRowIndexList.stream().forEach(rowIndex->{
               errorMap.put(rowIndex,errorMap.getOrDefault(rowIndex,"") + ";关键属性与系统中的重复" );
            });
         }
         //分类注入
         // batchSwitchClassifyAttrOnOrder(attrVOS,allCboList,classifyFullInfo,false);
         //boolean
         reSwitchBooleanAttrOnOrder(attrVOS,allCboList);
         //4.校验规则
         batchCheckVerifyOnOrder(attrVOS, allCboList,errorMap);
         //是否需要校验枚举/参照
         //5.校验枚举是否正确
         batchSwitchEnumAttrOnOrder(attrVOS, allCboList, errorMap);
         //7.处理参照的情况
         batchSwitchReferAttrOnOrder(attrVOS,allCboList,errorMap);
         //6.时间格式的验证
         //6.时间的,必须统一为yyyy-MM-dd HH:mm:ss
         batchSwitchDateAttrOnOrder(attrVOS,allCboList,errorMap);
         //设置默认值
         batchSwitchAttrDefault(attrVOS, allCboList);
         //最后弄组合规则
         batchSwitchComponentAttrOnOrder(attrVOS,allCboList);
         Map<String, ClientBusinessObject> rowIndexCboMap = allCboList.stream().filter(cbo -> cbo != null).collect(Collectors.toList()).stream().collect(Collectors.toMap(s -> s.getAttributeValue((IMPORT_ROW_INDEX)), t -> t));
         List<ClientBusinessObject> needSaveCboList = allCboList.stream().filter(cbo -> {
            String rowIndex = cbo.getAttributeValue(IMPORT_ROW_INDEX);
            return !errorMap.containsKey(rowIndex);
         }).collect(Collectors.toList());
         //相似校验
         Map<String,String>resembleMap=new HashMap<>();
         List<DataResembleVO> dataResembleVOS=new ArrayList<>();
         String btmtypeid= classifyFullInfo.getTopClassifyVO().getBtmtypeid();
         bathcResembleQuery(codeClassifyOid,templateVO,needSaveCboList,resembleMap,btmtypeid,dataResembleVOS);
         if(resembleMap.size()>0) {
            if(!CollectionUtils.isEmpty(dataResembleVOS)) {
               bladeRedis.set(uuid + "-resemble-data", dataResembleVOS);
               bladeRedis.expire(uuid + "-resemble-data",BATCHADD_REDIS_TIME);//redis过期时间
               // createRedisDatas(uuid + "-resemble", codeImprotDataVOS, resembleMap, false);
               //  wpResembleMap.putAll(resembleMap);
            }
         }
         //排除错误的,剩下正确的
         Map<String,String> newErrorMap=new HashMap<>();
         newErrorMap.putAll(resembleMap);
         newErrorMap.putAll(errorMap);
         needSaveCboList = allCboList.stream().filter(cbo -> {
            String rowIndex = cbo.getAttributeValue(IMPORT_ROW_INDEX);
            return !newErrorMap.containsKey(rowIndex);
         }).collect(Collectors.toList());
         if(newErrorMap.size()>0) {
            createRedisDatas(uuid + "-resemble",codeImprotDataVOS, newErrorMap,false);
         }
         createRedisDatas(uuid + "-ok",codeImprotDataVOS, newErrorMap,true);
      });
      //往物品节点上加模板
      List<String> needRowIndexList=new ArrayList<>();
      CodeImProtRusultVO codeImProtRusultVO = new CodeImProtRusultVO();
      if(errorMap.size()>0) {
         String filePath = returnErrorToExcel(dataSet.getRowData(), errorMap, needRowIndexList, dataSet.getColName());
         if (StringUtils.isNotBlank(filePath)) {
            codeImProtRusultVO.setFilePath(filePath);
         }
      }
      if(StringUtils.isNotBlank(uuid)){
         //将所有的分类存入缓存之中
         codeImProtRusultVO.setRedisUuid(uuid);
         /**  List<ColumnVO>columnVOList=new ArrayList<>();
          CodeImportTemplateVO wpCodeImportTemplateVO=new CodeImportTemplateVO();
          wpCodeImportTemplateVO.setRoot(true);
          wpCodeImportTemplateVO.setCodeClassifyTemplateVO(selectCodeClassifyTemplateVO);
          wpCodeImportTemplateVO.setCodeClassifyVO(classifyFullInfo.getCurrentClassifyVO());
          String templateOid=selectCodeClassifyTemplateVO.getOid();
          if(templateColumnVOMap.containsKey(templateOid)){
          columnVOList= columnVOList=templateColumnVOMap.get(templateOid);
          }else{
          createTemplate(selectCodeClassifyTemplateVO,templateColumnVOMap);
          columnVOList= columnVOList=templateColumnVOMap.get(templateOid);
          }
          wpCodeImportTemplateVO.setCloNamesList(columnVOList);
          codeImportTemplateVOS.add(wpCodeImportTemplateVO);
          if(wpResembleMap.size()>0){
          //  redisService.setCacheList(uuid + "-resemble-data", wpDataResembleVOList);
          createRedisDatas(uuid + "-resemble",selectCodeClassifyTemplateVO, wpCodeImprotDataVOList, wpResembleMap, false,codeClassifyOid);
          }
          //排除错误的,剩下正确的
          Map<String,String> newErrorMap=new HashMap<>();
          newErrorMap.putAll(wpResembleMap);
          newErrorMap.putAll(errorMap);
          List<CodeImprotDataVO>  needSaveCboList = wpCodeImprotDataVOList.stream().filter(cbo -> {
          String rowIndex = cbo.getRowIndex();
          return !newErrorMap.containsKey(rowIndex);
          }).collect(Collectors.toList());
          createRedisDatas(uuid + "-ok",selectCodeClassifyTemplateVO,wpCodeImprotDataVOList, newErrorMap,true,codeClassifyOid);****/
         if(codeImportTemplateVOS.size()>0){
            bladeRedis.set(uuid + "-class",codeImportTemplateVOS);
            bladeRedis.expire(uuid + "-class",BATCHADD_REDIS_TIME);
         }
      }
      return codeImProtRusultVO;
   }
   /**
    * 导入历史数据
    *
    * @param codeClassifyOid 分类的主键
    * @param classifyAttr 分类路径使用的属性
    * @param file            excel文件的信息
    * @return 有错误信息的excel
    */
   @Override
   public CodeImProtRusultVO batchImportHistoryData(String codeClassifyOid, String classifyAttr,File file) {
      VciBaseUtil.alertNotNull(codeClassifyOid,"分类的主键");
      ReadExcelOption reo = new ReadExcelOption();
      reo.setReadAllSheet(true);
      List<SheetDataSet> sheetDataSetList = ExcelUtil.readDataObjectFromExcel(file,null,reo);
      if (sheetDataSetList.size() > LIMIT + 1) {
         throw new VciBaseException("为了保证系统的稳定性,请一次不要导入超过1万条的数据");
      }
      Map<String,List<WriteExcelData>> shetNameMap=new HashMap<>();
      //相似项目查重
      String uuid=VciBaseUtil.getPk();
      boolean isCreateUUid=false;
      boolean isExport=false;
      for(int i=0;i<sheetDataSetList.size()-1;i++) {
         if (CollectionUtils.isEmpty(sheetDataSetList) || CollectionUtils.isEmpty(sheetDataSetList.get(i).getRowData())
            || sheetDataSetList.get(i).getRowData().size() < 1) {
            continue;
         }
         //历史导入的时候不处理编码
         //----逻辑内容----
         //1. 分类的路径可以在页面上选择是分类编号还是分类的名称
         //2. 分类的路径,必须是当前导入选择的分类的节点,以及其下级节点
         //3. 通过数据要导入的分类去查找对应的编码规则
         //4. 数据存储和批量申请一样,
         //5. 需要单独处理企业编码的内容,
         //     5.1 企业编码在当前excel里不能重复
         //     5.2 企业编码在系统中不能重复(可以是已经回收的)
         //     5.3 企业编码的长度,和编码规则的长度要对应上
         //     5.4 获取流水码段的值,去除填充的字符,看流水号是多少,然后将流水号和现在的最大流水号判断,小于就直接录入,大于则修改最大流水号
         //     5.5 存储企业编码到allcode中
         //查询分类和模板
         CodeClassifyFullInfoBO classifyFullInfo = classifyService.getClassifyFullInfo(codeClassifyOid);
         //先找到每一行的标题,然后根据标题来获取对应的属性
         SheetDataSet dataSet = sheetDataSetList.get(i);
         List<SheetRowData> rowDataList = dataSet.getRowData();
         //找第一行,为了找标题
         CodeClassifyTemplateVO templateVO = new CodeClassifyTemplateVO();
         /**  if (!templateService.checkChildHasSameTemplate(classifyFullInfo.getCurrentClassifyVO().getOid())) {
          throw new VciBaseException("当前的分类以及下级分类的模板不相同");
          }***/
         //都转换完了。需要批量检查
         //找所有的分类路径,需要校验路径是否正确,是否都在当前的分类的下级
         List<CodeClassifyVO> childClassifyVOs = classifyService.listChildrenClassify(codeClassifyOid, true, classifyAttr, true);
         Map<String/**路径**/, CodeClassifyVO> pathMap = Optional.ofNullable(childClassifyVOs).orElseGet(() -> new ArrayList<>()).stream().collect(Collectors.toMap(s -> s.getPath().startsWith("#") ? s.getPath().substring(1) : s.getPath(), t -> t));
         List<String> titleRowData = dataSet.getColName();
         Map<String, String> errorMap = new ConcurrentHashMap<>();
         pathMap.put("#current#",classifyFullInfo.getCurrentClassifyVO());
         try {
            List<CodeClassifyTemplateVO> templateVOList= checkSamesTemplate(titleRowData,sheetDataSetList,i,pathMap,errorMap);
            templateVO= templateVOList.get(0);
         }catch (Throwable e){
            throw  new VciBaseException(e.getMessage());
         }
         List<SheetRowData> needowDataList = rowDataList.stream().filter(cbo -> {
            String rowIndex = cbo.getRowIndex();
            return !errorMap.containsKey(rowIndex);
         }).collect(Collectors.toList());
         //checkTemplateSync(sheetDataSetList, templateVO,i);
         //这里不除去默认的属性
         List<CodeClassifyTemplateAttrVO> attrVOS = templateVO.getAttributes();
         Map<Integer/**列号**/, String/**字段的名称**/> fieldIndexMap = new HashMap<>();
         Map<String/**中文名称**/, String/**英文名称**/> attrNameIdMap = attrVOS.stream().collect(Collectors.toMap(s -> s.getName(), t -> t.getId()));
         String idFieldName = attrVOS.stream().filter(s -> VciQueryWrapperForDO.ID_FIELD.equalsIgnoreCase(s.getId())).findFirst().orElseGet(() -> new CodeClassifyTemplateAttrVO()).getName();
         getFieldIndexMap(titleRowData, attrNameIdMap, fieldIndexMap);
         //先不用管属性是否都存在,先转换一下数据
         List<ClientBusinessObject> cboList = new ArrayList<>();
         String fullPath = getFullPath(classifyFullInfo);
         //我们需要获取到所有的下级分类的oid的路径,因为后面需要
         Map<String/**主键**/, String/**路径**/> childOidPathMap = getChildClassifyPathMap(classifyFullInfo, fullPath);
         excelToCbo(classifyFullInfo, fieldIndexMap, needowDataList, templateVO, cboList, fullPath, false);
         Map<String/**主键**/, CodeClassifyVO> classifyVOMap = Optional.ofNullable(childClassifyVOs).orElseGet(() -> new ArrayList<>()).stream().collect(Collectors.toMap(s -> s.getOid(), t -> t));
         classifyVOMap.put(classifyFullInfo.getCurrentClassifyVO().getOid(), classifyFullInfo.getCurrentClassifyVO());
         pathMap.put("#current#", classifyFullInfo.getCurrentClassifyVO());
         //判断编号在excel里本身就重复的
         Map<String, Long> idCountMap = cboList.stream().collect(Collectors.groupingBy(ClientBusinessObject::getId, Collectors.counting()));
         List<String> repeatIdList = new ArrayList<>();
         idCountMap.forEach((id, count) -> {
            if (count > 1) {
               repeatIdList.add(id);
            }
         });
         if (!CollectionUtils.isEmpty(repeatIdList)) {
            cboList.stream().filter(s -> repeatIdList.contains(s.getId())).map(s -> s.getAttributeValue(IMPORT_ROW_INDEX)).forEach(rowIndex -> {
               errorMap.put(rowIndex, "编号在当前excel中重复;");
            });
         }
         //我们需要判断这些分类的模板是不是一样的,只需要校验,不用获取
         //检查分类的路径
         checkClassifyPathInHistory(cboList, errorMap, pathMap, childOidPathMap);
         //检查规则
         Map<String/**分类主键**/, String/**规则主键**/> ruleOidMap = new ConcurrentHashMap<String, String>();
         List<String> unExistRuleClassifyOidList = new CopyOnWriteArrayList<>();
         checkRuleOidInHistory(classifyVOMap, ruleOidMap, unExistRuleClassifyOidList);
         //如果出错了,我们依然执行有效的数据,无效的数据写回到excel中
         //我们根据出错的分类的主键,去找行号
         if (!CollectionUtils.isEmpty(unExistRuleClassifyOidList)) {
            cboList.stream().forEach(cbo -> {
               if (unExistRuleClassifyOidList.contains(cbo.getAttributeValue(CODE_CLASSIFY_OID_FIELD))) {
                  String row_index = cbo.getAttributeValue(IMPORT_ROW_INDEX);
                  errorMap.put(row_index, errorMap.getOrDefault(row_index, "") + ";根据分类路径对应的分类,没有设置编码规则");
               }
            });
         }
         //判断必输项
         batchCheckRequiredAttrOnOrder(templateVO, cboList, errorMap);
         //有限校验编码是否存在
         batchCheckIdExistOnOrder(templateVO, cboList, errorMap);
         //boolean
         reSwitchBooleanAttrOnOrder(attrVOS, cboList);
         // 枚举的内容需要根据名称转换为枚举的值
         batchSwitchEnumAttrOnOrder(attrVOS, cboList, errorMap);
         batchSwitchReferAttrOnOrder(attrVOS, cboList, errorMap);
         //6.处理分类注入
         batchSwitchClassifyAttrOnOrder(attrVOS, cboList, classifyFullInfo,true);
         //设置默认值
         batchSwitchAttrDefault(attrVOS, cboList);
         //7.处理组合规则
         batchSwitchComponentAttrOnOrder(attrVOS, cboList);
         //3.判断关键属性
         CodeImportResultVO keyResultVO = batchCheckKeyAttrOnOrder(classifyFullInfo, templateVO, cboList);
         Set<String> selfRepeatRowIndexList = keyResultVO.getSelfRepeatRowIndexList();
         Set<String> keyAttrRepeatRowIndexList = keyResultVO.getKeyAttrRepeatRowIndexList();
         if (!CollectionUtils.isEmpty(selfRepeatRowIndexList)) {
            selfRepeatRowIndexList.stream().forEach(rowIndex -> {
               errorMap.put(rowIndex, errorMap.getOrDefault(rowIndex, "") + ";在当前excel文件中关键属性重复");
            });
         }
         if (!CollectionUtils.isEmpty(keyAttrRepeatRowIndexList)) {
            keyAttrRepeatRowIndexList.stream().forEach(rowIndex -> {
               errorMap.put(rowIndex, errorMap.getOrDefault(rowIndex, "") + ";关键属性与系统中的重复");
            });
         }
         //4.校验规则
         batchCheckVerifyOnOrder(attrVOS, cboList, errorMap);
         //6.时间的,必须统一为yyyy-MM-dd HH:mm:ss
         batchSwitchDateAttrOnOrder(attrVOS, cboList, errorMap);
         if (CollectionUtils.isEmpty(ruleOidMap.values())) {
            throw new VciBaseException("导入的数据所选择的分类都没有设置编码规则");
         }
         Map<String, CodeRuleVO> ruleVOMap = ruleService.listCodeRuleByIds(ruleOidMap.values(), true).stream().collect(Collectors.toMap(s -> s.getOid(), t -> t));
         //校验编码规则和码段是否正确
         Map<String, List<String>> ruleRowIndexMap = new ConcurrentHashMap<>();
         checkSecLengthInHistory(cboList, classifyVOMap, ruleVOMap, ruleOidMap, errorMap, ruleRowIndexMap);
         ruleRowIndexMap.keySet().parallelStream().forEach(ruleOid -> {
            List<String> rowIndexList = ruleRowIndexMap.get(ruleOid);
            List<ClientBusinessObject> thisCbos = cboList.stream().filter(cbo -> rowIndexList.contains(cbo.getAttributeValue(IMPORT_ROW_INDEX)) && !errorMap.containsKey(cbo.getAttributeValue(IMPORT_ROW_INDEX))).collect(Collectors.toList());
//我们需要先查询一下,内容是否已经存在
            if(!CollectionUtils.isEmpty(thisCbos)){
               List<String> existIds = new ArrayList<>();
               VciBaseUtil.switchCollectionForOracleIn(thisCbos).stream().forEach(cbos -> {
                  List<CodeAllCode> codeAllCodeList= codeAllCodeService.selectByWrapper(Wrappers.<CodeAllCode>query().lambda().eq(CodeAllCode::getCodeRuleOid, ruleOid)
                     .notIn(CodeAllCode::getId,cbos.stream().map(s -> s.getId()).collect(Collectors.toSet()).toArray(new String[0]))
                     .notIn(CodeAllCode::getLcStatus,CodeAllCodeLC.TASK_BACK.getValue() + "','" + CodeAllCodeLC.OBSOLETED.getValue())
                  );
                  existIds.addAll(Optional.ofNullable(codeAllCodeList).orElseGet(() -> new ArrayList<>()).stream().map(s -> {
                     String id = s.getId();
                     if (StringUtils.isBlank(id)) {
                        id = s.getId();
                     }
                     return id;
                  }).collect(Collectors.toList()));
               });
               List<String> existIdCbos = thisCbos.stream().filter(s -> {
                  String id = s.getId();
                  if (StringUtils.isBlank(id)) {
                     id = s.getAttributeValue("id");
                  }
                  return existIds.contains(id);
               }).map(s -> s.getAttributeValue(IMPORT_ROW_INDEX)).collect(Collectors.toList());
               if (!CollectionUtils.isEmpty(existIdCbos)) {
                  thisCbos = thisCbos.stream().filter(s -> {
                     String id = s.getId();
                     if (StringUtils.isBlank(id)) {
                        id = s.getAttributeValue("id");
                     }
                     return !existIdCbos.contains(id);
                  }).collect(Collectors.toList());
                  existIdCbos.stream().forEach(rowIndex -> {
                     errorMap.put(rowIndex, errorMap.getOrDefault(rowIndex, "") + ";【" + idFieldName + "】在系统中已经被占用");
                  });
               }
            }
         });
         Map<String, ClientBusinessObject> rowIndexCboMap = cboList.stream().filter(cbo -> cbo != null).collect(Collectors.toList()).stream().collect(Collectors.toMap(s -> s.getAttributeValue((IMPORT_ROW_INDEX)), t -> t));
         if (errorMap.size() > 0) {
            isExport=true;
            createRedisDatas(uuid + "-error", templateVO, rowIndexCboMap, dataSet, fieldIndexMap, errorMap, false);
         }
         createWriteExcelData(rowDataList, errorMap, new ArrayList<>(), titleRowData, shetNameMap, templateVO);
         List<ClientBusinessObject> needSaveCboList = cboList.stream().filter(cbo -> {
            String rowIndex = cbo.getAttributeValue(IMPORT_ROW_INDEX);
            return !errorMap.containsKey(rowIndex);
         }).collect(Collectors.toList());
         //相似校验
         Map<String, String> resembleMap = new HashMap<>();
         List<DataResembleVO> dataResembleVOS = new ArrayList<>();
         String btmtypeid = classifyFullInfo.getTopClassifyVO().getBtmtypeid();
         bathcResembleQuery(templateVO.getCodeclassifyoid(), templateVO, needSaveCboList, resembleMap, btmtypeid, dataResembleVOS);
         if (resembleMap.size() > 0) {
            if (!CollectionUtils.isEmpty(dataResembleVOS)) {
               bladeRedis.set(uuid + "-resemble-data", dataResembleVOS);
               createRedisDatas(uuid + "-resemble", templateVO, rowIndexCboMap, dataSet, fieldIndexMap, resembleMap, false);
            }
         }
         //生成class缓存
         Map<String, String> rowIndexClsOidMap = cboList.stream().filter(cbo -> cbo != null).collect(Collectors.toList()).stream().collect(Collectors.toMap(s -> s.getAttributeValue((IMPORT_ROW_INDEX)), t -> t.getAttributeValue(CODE_CLASSIFY_OID_FIELD)));
         createRedisByCodeClassify(uuid + "-class",templateVO,dataSet,fieldIndexMap,true);
         //获取编码,查询在系统中是否被其他的引用了
         //排除错误的,剩下正确的
         Map<String, String> newErrorMap = new HashMap<>();
         newErrorMap.putAll(resembleMap);
         newErrorMap.putAll(errorMap);
         //要把以上的错误的都抛出后,再继续处理时间和组合规则
         needSaveCboList = cboList.stream().filter(cbo -> {
            String rowIndex = cbo.getAttributeValue(IMPORT_ROW_INDEX);
            return !newErrorMap.containsKey(rowIndex);
         }).collect(Collectors.toList());
         if((errorMap.size()>0&&needSaveCboList.size()>0)||resembleMap.size()>0){
            isCreateUUid=true;
         }
         List<String> needRowIndexList = needSaveCboList.stream().filter(s -> errorMap.containsKey(s.getAttributeValue(IMPORT_ROW_INDEX))).map(s -> s.getAttributeValue(IMPORT_ROW_INDEX)).collect(Collectors.toList());
         if (isExport||newErrorMap.size() > 0) {
            createRedisDatas(uuid + "-ok", templateVO, rowIndexCboMap, dataSet, fieldIndexMap, newErrorMap, true);
         } else {
            List<String> dataCBOIdList=new ArrayList<>();
            //SessionInfo sessionInfo = VciBaseUtil.getCurrentUserSessionInfo();
            List<ClientBusinessObject> finalNeedSaveCboList = needSaveCboList;
            CodeClassifyTemplateVO finalTemplateVO = templateVO;
            ruleRowIndexMap.keySet().parallelStream().forEach(ruleOid -> {
               //VciBaseUtil.setCurrentUserSessionInfo(sessionInfo);
               List<String> rowIndexList = ruleRowIndexMap.get(ruleOid);
               List<ClientBusinessObject> thisCbos = finalNeedSaveCboList.stream().filter(cbo -> rowIndexList.contains(cbo.getAttributeValue(IMPORT_ROW_INDEX)) && !errorMap.containsKey(cbo.getAttributeValue(IMPORT_ROW_INDEX))).collect(Collectors.toList());
               List<BaseModel> dataCBOList=new ArrayList<>();
               thisCbos.stream().forEach(clientBusinessObject -> {
                  BaseModel baseModel=new BaseModel();
                  BeanUtil.convert(clientBusinessObject,baseModel);
                  baseModel.setData(VciBaseUtil.objectToMapString(clientBusinessObject));
                  dataCBOList.add(baseModel);
                  dataCBOIdList.add(baseModel.getOid());
               });
               if (!CollectionUtils.isEmpty(thisCbos)) {
                  try {
                     productCodeService.productCodeAndSaveData(classifyFullInfo, finalTemplateVO, ruleVOMap.get(ruleOid), null, dataCBOList);
                  } catch (Throwable e) {
                     log.error("批量产生编码的时候出错了", e);
                     thisCbos.stream().forEach(cbo -> {
                        String rowIndex = cbo.getAttributeValue(IMPORT_ROW_INDEX);
                        errorMap.put(rowIndex, errorMap.getOrDefault(rowIndex, "") + ";系统错误,存储数据的时候出错了");
                     });
                  }
               }
            });
            engineService.batchSaveSelectChar(templateVO, dataCBOIdList);
         }
      }
      String excelFileName="";
      if(isExport&&!CollectionUtils.isEmpty(shetNameMap)) {
         excelFileName = LocalFileUtil.getDefaultTempFolder() + File.separator + "错误信息.xls";
         WriteExcelOption eo = new WriteExcelOption();
         shetNameMap.forEach((shetName, errorDataList) -> {
            eo.addSheetDataList(shetName, errorDataList);
         });
         try {
            new File(excelFileName).createNewFile();
         } catch (IOException e) {
            throw new VciBaseException(LangBaseUtil.getErrorMsg(e));
         }
         ExcelUtil.writeDataToFile(excelFileName, eo);
      }
      CodeImProtRusultVO codeImProtRusultVO=new CodeImProtRusultVO();
      if(StringUtils.isNotBlank(excelFileName)) {
         codeImProtRusultVO.setFilePath(excelFileName);
         codeImProtRusultVO.setFileOid("");
      }
      if(isCreateUUid){
         codeImProtRusultVO.setRedisUuid(uuid);
      }
      return codeImProtRusultVO;
   }
   /***
    * 从execl里构建对象
    * @param rowDataList
    * @param errorMap
    * @param needRowIndexList
    * @param titleRowData
    * @param shetNameMap
    * @param templateVO
    */
   private void createWriteExcelData(Collection<SheetRowData> rowDataList, Map<String,String> errorMap,
                             List<String> needRowIndexList, List<String> titleRowData, Map<String,List<WriteExcelData>> shetNameMap, CodeClassifyTemplateVO templateVO){
      List<WriteExcelData> errorDataList=new ArrayList<>();
      Map<String, SheetRowData> rowIndexDataMap = rowDataList.stream().filter(s -> !needRowIndexList.contains(s.getRowIndex())).collect(Collectors.toMap(s -> s.getRowIndex(), t -> t));
      errorDataList.add(new WriteExcelData(0,0,"错误信息"));
      for (int i = 0; i < titleRowData.size(); i++) {
         //错误信息在最后
         errorDataList.add(new WriteExcelData(0,i+1,titleRowData.get(i)));
      }
      Integer[] newRowIndex = new Integer[]{1};
      errorMap.forEach((index,error)->{
         //错误信息全部组合到一起
         SheetRowData rowData = rowIndexDataMap.getOrDefault(index, null);
         if(rowData!=null){
            errorDataList.add(new WriteExcelData(newRowIndex[0],0,error));
            rowData.getData().forEach((colIndex,value)->{
               errorDataList.add(new WriteExcelData(newRowIndex[0],colIndex+1,value));
            });
            newRowIndex[0]++;
         }
      });
      shetNameMap.put(templateVO.getName(),errorDataList);
   }
   /***
    *
    * @param currentTemplateVO
    * @param templateColumnVOMap
    */
   private void createTemplate(CodeClassifyTemplateVO currentTemplateVO,Map<String,List<ColumnVO>>templateColumnVOMap){
      List<CodeClassifyTemplateAttrVO> templateAttrVOS = currentTemplateVO.getAttributes().stream().filter(s ->
         !DEFAULT_ATTR_LIST.contains(s.getId())
            && StringUtils.isBlank(s.getComponentRule())
            && StringUtils.isBlank(s.getClassifyInvokeAttr())
            && (VciBaseUtil.getBoolean(s.getFormDisplayFlag()))
      ).collect(Collectors.toList());
      if(CollectionUtils.isEmpty(templateAttrVOS)){
         throw new VciBaseException("模板没有配置任何【表单显示】为【是】的属性");
      }
      List<ColumnVO> columnVOList=new ArrayList<>();
      ColumnVO errorMsgColumnVO=new ColumnVO();
      errorMsgColumnVO.setTitle("错误信息");
      errorMsgColumnVO.setField("errorMsg");
      columnVOList.add(errorMsgColumnVO);
      ColumnVO pathColumnVO=new ColumnVO();
      pathColumnVO.setTitle("分类路径");
      pathColumnVO.setField("codeclsfid");
      columnVOList.add(pathColumnVO);
      templateAttrVOS.stream().forEach(codetemplateAttr ->{
         String field=codetemplateAttr.getId();
         String name=codetemplateAttr.getName();
         ColumnVO columnVO=new ColumnVO();
         columnVO.setTitle(name);
         columnVO.setField(field);
         columnVO.setWidth(codetemplateAttr.getAttrTableWidth()==0?columnVO.getWidth():codetemplateAttr.getAttrTableWidth());
         columnVOList.add(columnVO);
      });
      templateColumnVOMap.put(currentTemplateVO.getOid(),columnVOList);
      log.info("模板"+currentTemplateVO.getName()+"对应的属性"+columnVOList.size());
   }
   /**
    * 错误信息返回excel
    * @param rowDataList 所有的导入数据
@@ -492,23 +1315,23 @@
         uuid="";
         //要把以上的错误的都抛出后,再继续处理时间和组合规则
         /*dataCBOList = cboList.stream().filter(cbo -> {
         needSaveCboList = cboList.stream().filter(cbo -> {
            String rowIndex = cbo.getAttributeValue(IMPORT_ROW_INDEX);
            return !newErrorMap.containsKey(rowIndex);
         }).collect(Collectors.toList());
*/         List<String> dataCBOIdList=new ArrayList<>();
         List<BaseModel> dataCBOList=new ArrayList<>();
         cboList.stream().forEach(clientBusinessObject -> {
            BaseModel baseModel=new BaseModel();
            BeanUtil.convert(clientBusinessObject,baseModel);
            baseModel.setData(VciBaseUtil.objectToMapString(clientBusinessObject));
            dataCBOList.add(baseModel);
            dataCBOIdList.add(baseModel.getOid());
         });
         if (!CollectionUtils.isEmpty(needSaveCboList)) {
            //9.我们处理业务数据
            //生成编码的内容
            List<String> dataCBOIdList=new ArrayList<>();
            List<BaseModel> dataCBOList=new ArrayList<>();
            cboList.stream().forEach(clientBusinessObject -> {
               BaseModel baseModel=new BaseModel();
               BeanUtil.convert(clientBusinessObject,baseModel);
               baseModel.setData(VciBaseUtil.objectToMapString(clientBusinessObject));
               dataCBOList.add(baseModel);
               dataCBOIdList.add(baseModel.getOid());
            });
            try {
               codeList = productCodeService.productCodeAndSaveData(classifyFullInfo,templateVO,ruleVO, orderDTO.getSecDTOList(),dataCBOList);
               //如果是编码生成失败,则直接就失败了,其他的判断出来有错误的我们都统一返回到excel里面
@@ -599,6 +1422,161 @@
      return dataGrid;
   }
   @Override
   public R batchImportData(List<CodeImprotSaveDatVO> codeImprotSaveDatVOList, String classifyAttr, boolean isImprot) {
      List<String> allNeedSaveCboList=new ArrayList<>();
      codeImprotSaveDatVOList.stream().forEach(codeImprotSaveDatVO -> {
         List<SheetRowData> rowDataList = new ArrayList<>();
         List<ClientBusinessObject>cboList=new ArrayList<>();
         List<String> colList=codeImprotSaveDatVO.getClos();
         CodeOrderDTO orderDTO= codeImprotSaveDatVO.getOrderDTO();
         List<Map<String, String>> dataList= codeImprotSaveDatVO.getDataList();
         Map<Integer, String> fieldIndexMap = new HashMap();
         for (int i=0;i<dataList.size();i++){
            SheetRowData sheetRowData=new SheetRowData();
            Map<String,String> dataMap= dataList.get(i);
            Map<Integer, String> data = new HashMap();
            final int[] colIndex = {0};
            Map<Integer, String> finalFieldIndexMap = new HashMap<>();
            dataMap.forEach((field, value)->{
               if(!ROW_INDEX.equalsIgnoreCase(field) && !ERROR_MSG.equalsIgnoreCase(field)){
                  data.put(colIndex[0],value);
                  finalFieldIndexMap.put(colIndex[0]++,field);
               }
            });
            fieldIndexMap=finalFieldIndexMap;
            sheetRowData.setData(data);
            sheetRowData.setRowIndex(i+"");
            rowDataList.add(sheetRowData);
         }
         CodeClassifyFullInfoBO classifyFullInfo = classifyService.getClassifyFullInfo(orderDTO.getCodeClassifyOid());
         log.info("分类:"+classifyFullInfo.getCurrentClassifyVO().getName()+"数据:"+codeImprotSaveDatVO.getDataList().size());
         // CodeClassifyTemplateVO codeClassifyTemplateVO=   engineService.getUsedTemplateByClassifyOid(orderDTO.getCodeClassifyOid());
         CodeClassifyTemplateVO codeClassifyTemplateVO=  templateService.getObjectHasAttrByOid(orderDTO.getTemplateOid());
         //规则的主键需要去获取
         CodeRuleVO ruleVO = engineService.getCodeRuleByClassifyFullInfo(classifyFullInfo);
         //除去默认的属性.还有只有表单显示的字段才导入
         List<CodeClassifyTemplateAttrVO> attrVOS = codeClassifyTemplateVO.getAttributes().stream().filter(s ->
            !DEFAULT_ATTR_LIST.contains(s.getId()) && VciBaseUtil.getBoolean(s.getFormDisplayFlag())
         ).collect(Collectors.toList());
         String fullPath = getFullPath(classifyFullInfo);
         excelToCbo(classifyFullInfo,fieldIndexMap,rowDataList, codeClassifyTemplateVO,cboList,fullPath,!isImprot);
         Map<String,String> errorMap=new HashMap<>();
         if(isImprot) {
            Map<String/**主键**/, String/**路径**/> childOidPathMap = getChildClassifyPathMap(classifyFullInfo, fullPath);
            //都转换完了。需要批量检查
            //找所有的分类路径,需要校验路径是否正确,是否都在当前的分类的下级
            List<CodeClassifyVO> childClassifyVOs = classifyService.listChildrenClassify(orderDTO.getCodeClassifyOid(), true, classifyAttr, true);
            Map<String/**路径**/, CodeClassifyVO> pathMap = Optional.ofNullable(childClassifyVOs).orElseGet(() -> new ArrayList<>()).stream().collect(Collectors.toMap(s -> s.getPath().startsWith("#") ? s.getPath().substring(1) : s.getPath(), t -> t));
            Map<String/**主键**/, CodeClassifyVO> classifyVOMap = Optional.ofNullable(childClassifyVOs).orElseGet(() -> new ArrayList<>()).stream().collect(Collectors.toMap(s -> s.getOid(), t -> t));
            classifyVOMap.put(classifyFullInfo.getCurrentClassifyVO().getOid(), classifyFullInfo.getCurrentClassifyVO());
            pathMap.put("#current#", classifyFullInfo.getCurrentClassifyVO());
            //我们需要判断这些分类的模板是不是一样的,只需要校验,不用获取
            //检查分类的路径
            checkClassifyPathInHistory(cboList, errorMap, pathMap, childOidPathMap);
         }
         //分类注入
         batchSwitchClassifyAttrOnOrder(attrVOS,cboList,classifyFullInfo,isImprot);
         //boolean
         reSwitchBooleanAttrOnOrder(attrVOS,cboList);
         //4.校验规则
         batchCheckVerifyOnOrder(attrVOS, cboList,errorMap);
         //5.校验枚举是否正确
         batchSwitchEnumAttrOnOrder(attrVOS, cboList, errorMap);
         //7.处理参照的情况
         batchSwitchReferAttrOnOrder(attrVOS,cboList,errorMap);
         //6.时间格式的验证
         //6.时间的,必须统一为yyyy-MM-dd HH:mm:ss
         batchSwitchDateAttrOnOrder(attrVOS,cboList,errorMap);
         //设置默认值
         batchSwitchAttrDefault(attrVOS, cboList);
         //最后弄组合规则
         batchSwitchComponentAttrOnOrder(attrVOS,cboList);
         //3.判断关键属性
         CodeImportResultVO keyResultVO = batchCheckKeyAttrOnOrder(classifyFullInfo, codeClassifyTemplateVO, cboList);
         Set<String> selfRepeatRowIndexList = keyResultVO.getSelfRepeatRowIndexList();
         Set<String> keyAttrRepeatRowIndexList = keyResultVO.getKeyAttrRepeatRowIndexList();
         if(!CollectionUtils.isEmpty(keyAttrRepeatRowIndexList)){
            keyAttrRepeatRowIndexList.stream().forEach(rowIndex->{
               errorMap.put(rowIndex,errorMap.getOrDefault(rowIndex,"") + ";关键属性与系统中的重复" );
            });
         }
         //4.校验规则
         batchCheckVerifyOnOrder(attrVOS, cboList,errorMap);
         //SessionInfo sessionInfo = VciBaseUtil.getCurrentUserSessionInfo();
         List<ClientBusinessObject>needSaveCboList = cboList.stream().filter(cbo -> {
            String rowIndex = cbo.getAttributeValue(IMPORT_ROW_INDEX);
            return !errorMap.containsKey(rowIndex);
         }).collect(Collectors.toList());
         log.info("分类:"+classifyFullInfo.getCurrentClassifyVO().getName()+"数据:"+needSaveCboList.size());
         if (!CollectionUtils.isEmpty(needSaveCboList)) {
            List<BaseModel> dataCBOList=new ArrayList<>();
            needSaveCboList.stream().forEach(clientBusinessObject -> {
               BaseModel baseModel=new BaseModel();
               BeanUtil.convert(clientBusinessObject,baseModel);
               baseModel.setData(VciBaseUtil.objectToMapString(clientBusinessObject));
               dataCBOList.add(baseModel);
               allNeedSaveCboList.add(baseModel.getOid());
            });
            try {
            //9.我们处理业务数据
               if (isImprot) {
                  productCodeService.productCodeAndSaveData(classifyFullInfo, codeClassifyTemplateVO, ruleVO, null, dataCBOList);
               }else {
                  productCodeService.productCodeAndSaveData(classifyFullInfo, codeClassifyTemplateVO, ruleVO, orderDTO.getSecDTOList(), dataCBOList);
               }
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
         //如果是编码生成失败,则直接就失败了,其他的判断出来有错误的我们都统一返回到excel里面
         engineService.batchSaveSelectChar(codeClassifyTemplateVO, allNeedSaveCboList);
      });
      return  R.success(isImprot?"批量历史导入成功":"批量申请成功");
   }
   /***
    *根据数据oid从缓存中移除数据
    * @param redisOid redisid
    * @param codeClssifyOid 存储规则的oid
    * @param dataOids  所需删除的数据
    * @return
    */
   @Override
   public R deleteDatas(String redisOid,String codeClssifyOid,String dataOids) {
      VciBaseUtil.alertNotNull(redisOid, "数据删除", redisOid, "数据缓存主键");
      VciBaseUtil.alertNotNull(codeClssifyOid, "数据删除", codeClssifyOid, "编码规则缓存主键");
      VciBaseUtil.alertNotNull(dataOids, "数据删除", dataOids, "所需删除的数据主键");
      try {
         List<CodeImprotDataVO> codeImprotDataVOs = bladeRedis.lRange(redisOid + "-" + codeClssifyOid,0,-1);
         List<String> dataOidList = new ArrayList<>();
         codeImprotDataVOs.stream().forEach(codeImprotDataVO -> {
            List<Map<String, String>> newDataList = new ArrayList<>();
            List<Map<String, String>> dataList = codeImprotDataVO.getDatas();
            dataList.stream().forEach(dataMap -> {
               String oid = dataMap.get("oid");
               if (!dataOidList.contains(oid)) {
                  newDataList.add(dataMap);
               }
            });
            codeImprotDataVO.setDatas(newDataList);
         });
         //重新缓存
         bladeRedis.del(redisOid + "-" + codeClssifyOid);
         bladeRedis.set(redisOid + "-" + codeClssifyOid, codeImprotDataVOs);
         bladeRedis.expire(redisOid + "-" + codeClssifyOid, BATCHADD_REDIS_TIME);
         return R.success("删除缓存数据成功");
      }catch (Throwable e){
         return R.fail("删除缓存数据失败!");
      }
   }
   /**
    * 集成批量申请数据
    * @param orderDTO 分类的主键
@@ -682,6 +1660,122 @@
      return fullPath;
   }
   /**
    * 检查码段的长度是否符合要求
    * @param cboList 数据
    * @param classifyVOMap 分类映射
    * @param ruleVOMap 规则对象
    * @param ruleOidMap 分类包含规则
    * @param errorMap 错误的信息
    * @param ruleRowIndexMap 规则包含的行号,key是规则主键,value是包含的全部行号
    */
   private void checkSecLengthInHistory(List<ClientBusinessObject> cboList,Map<String,CodeClassifyVO> classifyVOMap,Map<String,CodeRuleVO> ruleVOMap,
                               Map<String/**分类主键**/,String/**规则主键**/> ruleOidMap,Map<String,String> errorMap,Map<String,List<String>> ruleRowIndexMap){
      cboList.stream().forEach(cbo-> {
         String rowIndex = cbo.getAttributeValue(IMPORT_ROW_INDEX);
         String secLength = cbo.getAttributeValue(CODE_SEC_LENGTH_FIELD);
         //找分类
         String classifyOid = cbo.getAttributeValue(CODE_CLASSIFY_OID_FIELD);
         CodeClassifyVO classifyVO = classifyVOMap.get(classifyOid);
         if (classifyVO != null) {
            //2#2#4#1这样的方式
            CodeRuleVO ruleVO = ruleVOMap.getOrDefault(ruleOidMap.get(classifyVO.getOid()), null);
            if(ruleVO!=null){
               String[] secValues = secLength.split("#");
               //总长度和编码的长度
               String code = cbo.getAttributeValue(CODE_FIELD);
               if(code.length() != Arrays.stream(secValues).mapToInt(s->VciBaseUtil.getInt(s)).sum()){
                  errorMap.put(rowIndex,errorMap.getOrDefault(rowIndex,"") + ";码段宽度与系统中的编码规则不同" );
               }else if(secValues.length != ruleVO.getSecVOList().size()){
                  errorMap.put(rowIndex,errorMap.getOrDefault(rowIndex,"") + ";码段宽度与系统中的编码规则不同" );
               } else {
                  //每一个长度都不能超过码段的
                  boolean fined = false;
                  for (int j = 0; j < ruleVO.getSecVOList().size(); j++) {
                     CodeBasicSecVO secVO = ruleVO.getSecVOList().get(j);
                     String length= secValues[j];
                     if(StringUtils.isNotBlank(secVO.getCodeSecLength())&&VciBaseUtil.getInt(length)>VciBaseUtil.getInt(secVO.getCodeSecLength())){
                        errorMap.put(rowIndex,errorMap.getOrDefault(rowIndex,"") + ";码段宽度与系统中的编码规则不同" );
                        fined = true;
                        break;
                     }
                  }
                  /**for (int i = 0; i < secValues.length; i++) {
                   for (int j = 0; j < ruleVO.getSecVOList().size(); j++) {
                   CodeBasicSecVO secVO = ruleVO.getSecVOList().get(j);
                   if (VciBaseUtil.getInt(secValues[i]) > VciBaseUtil.getInt(secVO.getCodeSecLength())) {
                   errorMap.put(rowIndex,errorMap.getOrDefault(rowIndex,"") + ";码段宽度与系统中的编码规则不同" );
                   fined = true;
                   break;
                   }
                   }
                   if(fined){
                   break;
                   }
                   }***/
                  if(!fined){
                     //暂时不取流水的内容,因为调用produceCode的时候去处理
                     List<String> rowIndexList = ruleRowIndexMap.getOrDefault(ruleVO.getOid(), new ArrayList<>());
                     rowIndexList.add(rowIndex);
                     ruleRowIndexMap.put(ruleVO.getOid(),rowIndexList);
                  }
               }
            }else{
               errorMap.put(rowIndex,errorMap.getOrDefault(rowIndex,"") + ";分类没有设置编码规则" );
            }
         }
      });
   }
   /**
    * excel转换为cbo的对象
    * @param classifyFullInfo 分类的全部信息
    * @param codeImprotDataVO: 分类对应的数据
    * @param cboList 数据的列表
    * @param newCode 是否为批量申请
    */
   private void excelToCbo(CodeClassifyFullInfoBO classifyFullInfo,CodeImprotDataVO codeImprotDataVO,List<ClientBusinessObject> cboList, boolean newCode){
      String fullPath = getFullPath(classifyFullInfo);
      codeImprotDataVO.getDatas().stream().forEach(rowData -> {
         ClientBusinessObject cbo=new ClientBusinessObject();
         DefaultAttrAssimtUtil.addDefaultAttrAssimt(cbo, classifyFullInfo.getTopClassifyVO().getBtmtypeid());
         rowData.forEach((field,value)->{
            try {
               cbo.setAttributeValueWithNoCheck(field,value);
               if(WebUtil.isDefaultField(field)){
                  WebUtil.setValueToField(field, cbo, value);
               }
            } catch (VciBaseException e) {
               log.error("设置属性的值错误",e);
            }
         });
         try {
            cbo.setAttributeValue(CODE_TEMPLATE_OID_FIELD,codeImprotDataVO.getTemplateOid());
            cbo.setAttributeValue(IMPORT_ROW_INDEX,rowData.get(IMPORT_ROW_INDEX));
            if(newCode){
               cbo.setAttributeValue(CODE_CLASSIFY_OID_FIELD,classifyFullInfo.getCurrentClassifyVO().getOid());
               cbo.setAttributeValue(CODE_FULL_PATH_FILED,fullPath);
               //cbo.setLcStatus(CodeDefaultLC.EDITING.getValue());
               /*int secret = VciBaseUtil.getInt(cbo.getAttributeValue(SECRET_FIELD));
               if(secret == 0 || !secretService.checkDataSecret(secret) ){
                  Integer userSecret = VciBaseUtil.getCurrentUserSecret();
                  cbo.setAttributeValue(SECRET_FIELD,String.valueOf((userSecret==null || userSecret ==0)? UserSecretEnum.NONE.getValue():userSecret));
               }*/
            }else{
               //此时还没有转换路径
               //cbo.setAttributeValue(CODE_FULL_PATH_FILED, childOidPathMap.getOrDefault(rowData.getData().getOrDefault(CODE_CLASSIFY_OID_FIELD,""),fullPath));
               cbo.setLcStatus(CodeDefaultLC.RELEASED.getValue());
            }
            rowData.put("oid",cbo.getOid());
         }catch (Throwable e){
            log.error("设置默认的属性的值错误",e);
         }
         cboList.add(cbo);
      });
   }
   /**
    * excel转换为cbo的对象
    * @param classifyFullInfo 分类的全部信息
@@ -817,6 +1911,32 @@
         });
      }
   }
   /**
    * 系统模板中默认值设置
    * @param attrVOS 模板属性
    * @param dataList excel的数据内容
    */
   private void batchSwitchAttrDefault(Collection<CodeClassifyTemplateAttrVO> attrVOS,List<ClientBusinessObject> dataList) {
      Map<String, CodeClassifyTemplateAttrVO> dateAttrVOMap = attrVOS.stream().filter(s -> StringUtils.isNotBlank(s.getDefaultValue())).collect(Collectors.toMap(s -> s.getId().toLowerCase(Locale.ROOT), t -> t));
      if(!CollectionUtils.isEmpty(dateAttrVOMap)) {
         dateAttrVOMap.forEach((attrId, attrVO) -> {
            String defaultValue = attrVO.getDefaultValue();
            dataList.stream().forEach(cbo -> {
               String dataValue= cbo.getAttributeValue(attrId);
               if(StringUtils.isBlank(dataValue)){
                  dataValue=defaultValue;
               }
               try {
                  cbo.setAttributeValue(attrId, dataValue);
               }catch (Throwable e){
                  log.error("设置属性的错误",e);
               }
            });
         });
      }
   }
   /**
    * 转移boolean型的属性
    * @param attrVOS 属性的对象
@@ -968,7 +2088,136 @@
      }
   }
   /**
    * 批量检查企业编码是否存在
    * @param templateVO 模板的显示对象
    * @param cboList 数据的列表
    * @param errorMap 错误的信息
    */
   private void batchCheckIdExistOnOrder(CodeClassifyTemplateVO templateVO,List<ClientBusinessObject> cboList,Map<String ,String> errorMap){
      List<String> existIds = new ArrayList<>();
      VciBaseUtil.switchCollectionForOracleIn(cboList).stream().forEach(cbos -> {
         Map<String, String> conditionMap = new HashMap<>();
         conditionMap.put("id", QueryOptionConstant.IN + "(" + VciBaseUtil.toInSql(cbos.stream().map(s -> s.getId()).collect(Collectors.toSet()).toArray(new String[0])) + ")");
         R<BtmTypeVO>  r= btmTypeClient.getDetail(templateVO.getBtmTypeId());
         BtmTypeVO btmTypeVO =r.getData();
         String tableName=btmTypeVO.getTableName();
         StringBuffer sb=new StringBuffer();
         sb.append(" select id from ");
         sb.append(tableName);
         sb.append(" where 1=1 ");
         sb.append(" id in (");
         sb.append(VciBaseUtil.toInSql(cbos.stream().map(s -> s.getId()).collect(Collectors.toSet()).toArray(new String[0])));
         sb.append(")");
         List<String> idList= commonsMapper.selectById(sb.toString());
         //业务数据如果码值回收会直接删除数据,所以这里直接判断是否存在即可
         existIds.addAll(Optional.ofNullable(idList).orElseGet(() -> new ArrayList<>()).stream().map(s -> s.toLowerCase(Locale.ROOT)).collect(Collectors.toList()));
      });
      if(!CollectionUtils.isEmpty(existIds)){
         String idFieldName = templateVO.getAttributes().stream().filter(s -> VciQueryWrapperForDO.ID_FIELD.equalsIgnoreCase(s.getId())).findFirst().orElseGet(() -> new CodeClassifyTemplateAttrVO()).getName();
         if(StringUtils.isBlank(idFieldName)){
            idFieldName = "企业编码";
         }
         String finalIdFieldName = idFieldName;
         cboList.stream().forEach(cbo->{
            String id = cbo.getId();
            if(StringUtils.isBlank(id)){
               id = cbo.getAttributeValue("id");
            }
            if(existIds.contains(id)){
               String rowIndex = cbo.getAttributeValue(IMPORT_ROW_INDEX);
               String msg = errorMap.getOrDefault(rowIndex, "");
               msg+=";" + finalIdFieldName + "的值在系统中已经存在";
               errorMap.put(rowIndex,msg);
            }
         });
      }
   }
   /***
    * 校验分类对应的模板信息
    * @param titleRowData
    * @param sheetDataSetList
    * @param shetNumber
    * @param pathMap
    * @param errorMap
    * @return
    * @throws Throwable
    */
   private LinkedList<CodeClassifyTemplateVO> checkSamesTemplate(List<String> titleRowData,  List<SheetDataSet> sheetDataSetList,int shetNumber,Map<String/**路径**/, CodeClassifyVO> pathMap,Map<String,String>errorMap) throws Throwable {
      Map<String,String>pathOidMap =new HashMap<>();
      Map<String,String> templateIdRowIndex=new HashedMap();
      SheetDataSet dataSet=  sheetDataSetList.get(shetNumber);
      LinkedHashMap<String,CodeClassifyTemplateVO> codeClassifyTemplateVOMap=new LinkedHashMap <String,CodeClassifyTemplateVO>();
      for (int i=0; i<titleRowData.size();i++){
         String title= titleRowData.get(i);
         if(title.equals("分类路径")) {
            int finalI = i;
            dataSet.getRowData().stream().forEach(sheetRowData -> {
               String Path = sheetRowData.getData().get(finalI);
               String rowIndex=sheetRowData.getRowIndex();
               if(StringUtils.isBlank(Path)){
                  Path= "#current#";
               }
               CodeClassifyTemplateVO newTemplateVO=new CodeClassifyTemplateVO();
               String templateOid="";
               if(pathOidMap.containsKey(Path)){
                  templateOid= pathOidMap.get(Path) ;
                  newTemplateVO=codeClassifyTemplateVOMap.get(templateOid);
               }else{
                  if (pathMap.containsKey(Path)) {
                     CodeClassifyVO codeClassifyVO = pathMap.get(Path);
                     newTemplateVO = engineService.getUsedTemplateByClassifyOid(codeClassifyVO.getOid());
                     if (newTemplateVO != null) {
                        templateOid = newTemplateVO.getOid();
                     } else {
                        errorMap.put(rowIndex, "第" + rowIndex + "行,分类路径未查询到相应的分类模板");
                     }
                  } else {
                     errorMap.put(rowIndex, "第" + rowIndex + "行,分类路径未查询到相应的分类");
                  }
               }
               pathOidMap.put(Path, templateOid);
               codeClassifyTemplateVOMap.put(templateOid, newTemplateVO);
               templateIdRowIndex.put(templateOid, templateIdRowIndex.getOrDefault(templateOid, "") + "," +rowIndex );
            });
            break;
         }
      }
      LinkedList<CodeClassifyTemplateVO> codeClassifyTemplateVOList=new LinkedList<>();
      StringBuffer sb=new StringBuffer();
      codeClassifyTemplateVOMap.keySet().forEach(tempateOid->{
         String templateOidInExcel="";
         String tempateName="";
         CodeClassifyTemplateVO t= codeClassifyTemplateVOMap.get(tempateOid);
         codeClassifyTemplateVOList.add(t);
         if(!CollectionUtils.isEmpty(sheetDataSetList)
            && sheetDataSetList.size()>1 && !CollectionUtils.isEmpty(sheetDataSetList.get(sheetDataSetList.size()-1).getColName())){
            List<SheetRowData>  rowData=  sheetDataSetList.get(sheetDataSetList.size()-1).getRowData();
            templateOidInExcel=rowData.get(shetNumber).getData().get(0);
            tempateName=rowData.get(shetNumber).getData().get(2);
            //templateOidInExcel = sheetDataSetList.get(sheetDataSetList.size()-1).getColName().get(sheetDataSetList.size()-i);
         }
         if(StringUtils.isBlank(templateOidInExcel) || !templateOidInExcel.equalsIgnoreCase(tempateOid)){
            sb.append("模板【"+tempateName+"】中第"+templateIdRowIndex.get(tempateOid)+"行数据不属于当前模板的数据,请核对!");
         }
      });
      if(StringUtils.isNotBlank(sb.toString())){
         throw  new Throwable(sb.toString());
      }
      if(codeClassifyTemplateVOList.size()>1){
         String message="模板【"+dataSet.getSheetName()+"】根据分类路径判断,分类存在多个模板";
         throw  new Throwable(message);
      }
      if(codeClassifyTemplateVOList.size()==0){
         String message="模板【"+dataSet.getSheetName()+"】根据数据分类路径判断,未匹配到对应模板";
         throw  new Throwable(message);
      }
      return codeClassifyTemplateVOList ;
   }
   /**
    * 从属性上获取参照的内容
    * @param attrVO 属性的信息
@@ -1211,6 +2460,109 @@
      //resultVO.setSuccess(true);
      return resultVO;
   }
   /**
    * 检查分类的路径是否存在
    * @param cboList 业务数据
    * @param errorMap 错误信息
    * @param pathMap 路径和分类的映射
    */
   private void checkClassifyPathInHistory(List<ClientBusinessObject> cboList,
                                 Map<String,String> errorMap,     Map<String/**路径**/,CodeClassifyVO> pathMap,
                                 Map<String/**主键**/, String/**路径**/> childOidPathMap) {
      cboList.parallelStream().forEach(cbo -> {
         String classifyPath = cbo.getAttributeValue(CODE_CLASSIFY_OID_FIELD);
         //如果path为空,则表示是导入当前分类
         if(StringUtils.isBlank(classifyPath)){
            classifyPath = "#current#";
         }
         if ( !pathMap.containsKey(classifyPath)) {
            String row_index = cbo.getAttributeValue(IMPORT_ROW_INDEX);
            errorMap.put(row_index,errorMap.getOrDefault(row_index,"") + ";分类路径不存在");
         } else {
            //转一下分类的主键
            try {
               String classifyOid = pathMap.get(classifyPath).getOid();
               cbo.setAttributeValueWithNoCheck(CODE_CLASSIFY_OID_FIELD, classifyOid);
               cbo.setAttributeValue(CODE_FULL_PATH_FILED, childOidPathMap.getOrDefault(classifyOid,classifyPath));
            } catch (VciBaseException e) {
               log.error("设置属性的错误", e);
            }
         }
      });
   }
   /**
    * 检查分类以及子分类是否都有编码规则
    * @param classifyVOMap 分类的显示对象映射
    * @param ruleOidMap 规则的主键映射
    * @param unExistRuleClassifyOidList 不存在编码规则的分类的主键
    */
   private void checkRuleOidInHistory( Map<String/**主键**/,CodeClassifyVO> classifyVOMap,  Map<String/**分类主键**/,String/**规则主键**/> ruleOidMap,
                              List<String> unExistRuleClassifyOidList   ){
      if(!CollectionUtils.isEmpty(classifyVOMap)){
         classifyVOMap.values().parallelStream().forEach(classifyVO->{
            if(StringUtils.isNotBlank(classifyVO.getCoderuleoid())){
               ruleOidMap.put(classifyVO.getOid(),classifyVO.getCoderuleoid());
            }else{
               //递归找上级
               List<String> ruleOidList = new ArrayList<>();
               recursionRule(classifyVOMap,classifyVO.getParentcodeclassifyoid(),ruleOidList);
               if(!CollectionUtils.isEmpty(ruleOidList)){
                  ruleOidMap.put(classifyVO.getOid(),ruleOidList.get(0));
               }else{
                  unExistRuleClassifyOidList.add(classifyVO.getOid());
               }
            }
         });
      }
      log.info(";;;;");
   }
   /**
    * 递归找编码规则
    * @param classifyVOMap 分类的显示对象映射
    * @param classifyOid 分类的主键
    * @param ruleOidList 规则的主键list
    */
   private void recursionRule(Map<String, CodeClassifyVO> classifyVOMap,String classifyOid,List<String> ruleOidList){
      if(classifyVOMap.containsKey(classifyOid)){
         CodeClassifyVO classifyVO = classifyVOMap.get(classifyOid);
         if(StringUtils.isNotBlank(classifyVO.getCoderuleoid())){
            ruleOidList.add(classifyVO.getCoderuleoid());
            return;
         }else{
            recursionRule(classifyVOMap,classifyVO.getParentcodeclassifyoid(),ruleOidList);
         }
      }else{
         Map<String, CodeClassifyVO> parentClassifyVOMap=new HashMap<>();
         CodeClassifyVO codeClassifyVO= this.classifyService.getObjectByOid(classifyOid);
         parentClassifyVOMap.put(codeClassifyVO.getOid(),codeClassifyVO);
         recursionRule(parentClassifyVOMap,codeClassifyVO.getOid(),ruleOidList);
      }
   }
   /**
    * 获取子分类的路径
    * @param classifyFullInfo 分类全部信息
    * @param fullPath 分类的全路径
    * @return 子分类的路径,key是分类的主键
    */
   private Map<String/**分类的主键**/,String/**分类路径**/> getChildClassifyPathMap(CodeClassifyFullInfoBO classifyFullInfo,String fullPath){
      List<CodeClassifyVO> childPathVOs = classifyService.listChildrenClassify(classifyFullInfo.getCurrentClassifyVO().getOid(), true, VciQueryWrapperForDO.OID_FIELD, true);
      Map<String/**分类的主键**/,String/**分类的主键**/> childOidPathMap = new ConcurrentHashMap<>();
      if(!CollectionUtils.isEmpty(childPathVOs)){
         childPathVOs.parallelStream().forEach(childPath->{
            // String thisClassifyPath = fullPath + "##" + childPath.getPath().replace("#" + classifyFullInfo.getCurrentClassifyVO().getOid() + "#","").replace("#","##");
            List<String> list=Arrays.asList(childPath.getPath().split("#"));
            List<String> newPahtList=  list.stream().sorted(Comparator.comparing(s -> s,Comparator.reverseOrder())).collect(Collectors.toList());
            String thisClassifyPath=StringUtils.join(newPahtList,"##")+fullPath;
            childOidPathMap.put(childPath.getOid(),thisClassifyPath);
         });
      }
      return childOidPathMap;
   }
   /**
    * 获取导入的内容中关键属性重复的行号
    * @param ketAttrMap 关键属性的映射
@@ -1301,6 +2653,126 @@
   }
   /***
    * 根据不同模板组织execl数据
    * @param dataSet
    * @param pathMap
    * @param errorMap
    */
   private void createExeclClassData(SheetDataSet dataSet,Map<String/**路径**/, CodeClassifyVO> pathMap,Map<String,String>errorMap,List<CodeImprotDataVO> codeClassifyDatas){
      Map<String,CodeImprotDataVO> pathDatas=new HashMap<>();
      List<String> titleRowData= dataSet.getColName();
      List<SheetRowData>  rowDataList= dataSet.getRowData();
      LinkedHashMap<String,CodeClassifyTemplateVO> codeClassifyTemplateVOMap=new LinkedHashMap <String,CodeClassifyTemplateVO>();
      LinkedHashMap<String,CodeRuleVO> codeRuleVOVOMap=new LinkedHashMap <String,CodeRuleVO>();
      for (int i=0;i<titleRowData.size();i++){
         String title= titleRowData.get(i);
         if(title.equals("分类路径")) {
            int finalI = i;
            rowDataList.stream().forEach(sheetRowData -> {
               CodeImprotDataVO dataVO=new CodeImprotDataVO();
               String Path = sheetRowData.getData().get(finalI);
               String rowIndex=sheetRowData.getRowIndex();
               Map<Integer, String> execlData= sheetRowData.getData();
               CodeClassifyTemplateVO newTemplateVO=new CodeClassifyTemplateVO();
               CodeRuleVO codeRuleVO=new CodeRuleVO();
               if(StringUtils.isEmpty(Path)){
                  Path="#current#";
               }
               if(pathMap.containsKey(Path)){
                  CodeClassifyVO codeClassifyVO=pathMap.get(Path);
                  if(codeClassifyTemplateVOMap.containsKey(Path)){
                     newTemplateVO=  codeClassifyTemplateVOMap.get(Path);
                     codeRuleVO=  codeRuleVOVOMap.get(Path);
                     if(newTemplateVO==null||StringUtils.isBlank(newTemplateVO.getOid())){
                        errorMap.put(rowIndex,"第"+rowIndex+"行,分类路径未查询到相应的分类模板");
                     }
                     if(codeRuleVO==null||StringUtils.isBlank(codeRuleVO.getOid())){
                        errorMap.put(rowIndex,"第"+rowIndex+"行,分类路径未查询到相应的分类规则");
                     }
                  }else{
                     newTemplateVO =engineService.getUsedTemplateByClassifyOid(codeClassifyVO.getOid());
                     if(newTemplateVO==null||StringUtils.isBlank(newTemplateVO.getOid())){
                        errorMap.put(rowIndex,"第"+rowIndex+"行,分类路径未查询到相应的分类模板");
                     }
                     codeRuleVO=engineService.getCodeRuleByClassifyOid(codeClassifyVO.getOid());
                     if(codeRuleVO==null||StringUtils.isBlank(codeRuleVO.getOid())){
                        errorMap.put(rowIndex,"第"+rowIndex+"行,分类路径未查询到相应的分类规则");
                     }
                  }
                  if(pathMap.containsKey(Path)){
                     dataVO=pathDatas.getOrDefault(Path,dataVO);
                  }
                  dataVO.setTemplateOid(newTemplateVO==null?"":newTemplateVO.getOid());
                  dataVO.setCodeClassifyTemplateVO(newTemplateVO);
                  dataVO.setCodeClassifyVO(codeClassifyVO);
                  dataVO.setCodeRuleVO(codeRuleVO);
                  dataVO.setRowIndex(rowIndex);
                  dataVO.setCodeClassifyOid(codeClassifyVO.getOid());//设置分类oid
                  dataVO.setCodeRuleOid(codeRuleVO==null?"":codeRuleVO.getOid());
                  createExeclClassData(titleRowData,newTemplateVO,execlData,dataVO);
                  pathDatas.put(Path,dataVO);
                  codeClassifyTemplateVOMap.put(Path, newTemplateVO);
                  codeRuleVOVOMap.put(Path,codeRuleVO);
               }else{
                  errorMap.put(rowIndex,"第"+rowIndex+"行,分类路径未查询到相应的分类");
               }
            });
            break;
         }
      }
      List <CodeImprotDataVO> newCodeImprotDataVO= pathDatas.values().stream().collect(Collectors.toList());
      codeClassifyDatas.addAll(newCodeImprotDataVO);
      log.info("222");
   }
   /***
    *  @param titleRowData
    * @param newTemplateVO
    * @param execlData
    * @param codeImprotDataVO
    */
   private void createExeclClassData(List<String> titleRowData, CodeClassifyTemplateVO newTemplateVO, Map<Integer, String> execlData, CodeImprotDataVO codeImprotDataVO){
      //除去默认的属性.还有只有表单显示的字段才导入
      List<CodeClassifyTemplateAttrVO> attrVOS = newTemplateVO.getAttributes().stream().filter(s ->
         !DEFAULT_ATTR_LIST.contains(s.getId()) && VciBaseUtil.getBoolean(s.getFormDisplayFlag())
      ).collect(Collectors.toList());
      Map<String/**中文名称**/, String/**英文名称**/> attrNameIdMap = attrVOS.stream().collect(Collectors.toMap(s -> s.getName(), t -> t.getId()));
      List<String> fields=new ArrayList<>();
      Map<String,String> filedValueMap=new HashMap<>();
      List<String> colNames=new ArrayList<>();
      for (int i = 0; i < titleRowData.size(); i++) {
         String title = titleRowData.get(i);
         title=title.replace(KEY_ATTR_CHAR,"").replace(REQUIRED_CHAR,"");
         String id = attrNameIdMap.getOrDefault(title,"");
         if(StringUtils.isBlank(id) && "分类路径".equalsIgnoreCase(title)){
            id = CODE_CLASSIFY_OID_FIELD;
         }
         if(StringUtils.isBlank(id) && "码段宽度".equalsIgnoreCase(title)){
            id = CODE_SEC_LENGTH_FIELD;
         }
         if(StringUtils.isBlank(id) && "企业编码".equalsIgnoreCase(title)){
            id = CODE_FIELD;
         }
         if(StringUtils.isNotBlank(id)){
            // fieldIndexMap.put(i,id);id
            fields.add(id);
            colNames.add(title);
            String value= StringUtils.isNotBlank(execlData.get(i))?execlData.get(i):"";
            filedValueMap.put(id,value);
         }
      }
      // filedValueMap.put(CODE_CLASSIFY_OID_FIELD,codeImprotDataVO.getCodeClassifyOid());//将当前分类oid存入字段中
      filedValueMap.put("codeclassifyid",codeImprotDataVO.getCodeClassifyOid());
      filedValueMap.put(IMPORT_ROW_INDEX,codeImprotDataVO.getRowIndex());
      filedValueMap.put("codetemplateoid",newTemplateVO.getOid());
      codeImprotDataVO.setFields(fields);
      codeImprotDataVO.setColNames(colNames);
      codeImprotDataVO.getDatas().add(filedValueMap);
   }
   /***
    * 正确错误数据redis缓存
    * @param uuid
    * @param templateVO
@@ -1359,6 +2831,50 @@
      }
   }
   /******
    * 根据编码规则缓存数据
    * @param uuid
    * @param codeImprotDataVOs
    * @param errorMap
    * @param isok
    */
   private void createRedisDatas(String uuid, List<CodeImprotDataVO> codeImprotDataVOs, Map<String, String> errorMap, boolean isok){
      codeImprotDataVOs.stream().forEach(codeImprotDataVO -> {
         List<Map<String, String>>  dataLists=new ArrayList<>();
         CodeImprotDataVO newCodeImprotDataVO=new CodeImprotDataVO();
         if(errorMap.size()>0) {
            //要把以上的错误的都抛出后,再继续处理时间和组合规则
            dataLists = codeImprotDataVO.getDatas().stream().filter(cbo -> {
               String rowIndex=cbo.get(IMPORT_ROW_INDEX);
               String msg=StringUtils.isBlank(errorMap.get(rowIndex))?"":errorMap.get(rowIndex);
               cbo.put("errorMsg",msg);
               return  isok? !errorMap.containsKey(rowIndex):errorMap.containsKey(rowIndex);
            }).collect(Collectors.toList());
         }else{
            dataLists= codeImprotDataVO.getDatas();
         }
         BeanUtilForVCI.copyPropertiesIgnoreCase(codeImprotDataVO,newCodeImprotDataVO);
         newCodeImprotDataVO.setDatas(dataLists);
         List<CodeImprotDataVO> codeImprotDataVOList=new ArrayList<>();
         codeImprotDataVOList.add(newCodeImprotDataVO);
         /***update 更改成以规则分组*****/
         String codeRuleOid=codeImprotDataVO.getCodeRuleOid();
         log.info(uuid+"-"+codeRuleOid+":条目数"+codeImprotDataVOList.size());
         if(codeImprotDataVOList.size()>0) {
            bladeRedis.set(uuid + "-" + codeRuleOid, codeImprotDataVOList);
            bladeRedis.expire(uuid + "-" + codeRuleOid, BATCHADD_REDIS_TIME);//redis过期时间
         }
         /*** String codeClassifyOid=codeImprotDataVO.getCodeClassifyOid();
          redisService.setCacheList(uuid+"-"+codeClassifyOid,codeImprotDataVOList);
          logger.info(uuid+"-"+codeClassifyOid+":条目数"+codeImprotDataVOList.size());
          redisService.expire(uuid+"-"+codeClassifyOid,BATCHADD_REDIS_TIME);//redis过期时间***/
      });
   }
   /****
    * 数据相似项数据校验redis缓存
    * @param codeClassifyOid