Source/plt-web/plt-web-parent/plt-web/src/main/java/com/vci/frameworkcore/compatibility/impl/OrgDeptQueryServiceImpl.java
@@ -99,14 +99,14 @@
   }
   /**
    * 根据父部门主键获取和部门名称部门的信息
    * 根据父部门主键获取部门名称部门的信息
    * @param parentDeptOid 父部门主键
    * @param conditionMap 部门名称
    * @return 部门的显示对象,如果部门不存在则返回null,不会抛出异常
    * @throws VciBaseException 参数为空或者数据库存在问题的时候会抛出异常
    */
   @Override
   public List<OrgDepartmentVO> getDeptByDeptPOidAndCondition(String parentDeptOid,Map<String,String> conditionMap) throws VciBaseException {
   public List<OrgDepartmentVO> getDeptByDeptpOidAndCondition(String parentDeptOid,Map<String,String> conditionMap) throws VciBaseException {
      if(Func.isEmpty(conditionMap)){
         conditionMap = new HashMap();
      }
@@ -122,6 +122,42 @@
         return null;
      }
      return deptDO2VOs(roleForPlatform1s);
   }
   /**
    * 根据父部门名称路径获取和部门名称部门的信息(主要用于导入时根据部门命令全路径和名称或编号查重)
    * @return 部门的显示对象,如果部门不存在则返回null,不会抛出异常
    * @throws VciBaseException 参数为空或者数据库存在问题的时候会抛出异常
    */
   @Override
   public List<OrgDepartmentVO> getDeptAllFullName() throws VciBaseException {
      //查询部门信息,主要查询部门名称组成全路径
      String sql = "SELECT  PLUID, PLNAME, PLNUM, PLSTATUS, level, substr(sys_connect_by_path(PLNAME, '/'), 2) AS FULL_NAME_PATH " +
            "FROM PLDEPT " +
            "START WITH PLPARENTUID IS NULL " +
            "CONNECT BY PRIOR PLUID = PLPARENTUID";
      List<BusinessObject> cbos = boService.queryByOnlySql(sql);
      List<OrgDepartmentVO> departmentVOList = new ArrayList<>();
      if(!CollectionUtils.isEmpty(cbos)){
         cbos.stream().forEach(cbo->{
            //部门主键
            String deptOid = ObjectTool.getNewBOAttributeValue(cbo,"PLUID");
            //部门名称
            String deptName = ObjectTool.getNewBOAttributeValue(cbo, "PLNAME");
            //部门编号
            String deptNum = ObjectTool.getNewBOAttributeValue(cbo, "PLNUM");
            //部门层级
            String deptLevel = ObjectTool.getNewBOAttributeValue(cbo, "level");
            //部门名称全路径
            String fullDeptNamePath = ObjectTool.getNewBOAttributeValue(cbo, "FULL_NAME_PATH");
            //部门状态
            String deptStatus = ObjectTool.getNewBOAttributeValue(cbo, "PLSTATUS");
            OrgDepartmentVO orgDepartmentVO = new OrgDepartmentVO(deptNum,deptName,Short.valueOf(deptStatus),fullDeptNamePath,Integer.valueOf(deptLevel));
            orgDepartmentVO.setOid(deptOid);
            departmentVOList.add(orgDepartmentVO);
         });
      }
      return departmentVOList;
   }
   /**
@@ -147,7 +183,7 @@
      if(deptForPlatform1!=null){
         departmentVO.setOid(deptForPlatform1.getPluid());
         // 除部门主键外的唯一标识,长度为Dept:+8位可通过该参数反推回部门主键的hash编码
         departmentVO.setUniqueId("Dept:"+Func.oidEnHash(deptForPlatform1.getPluid()));
         //departmentVO.setUniqueId("Dept:"+Func.oidEnHash(deptForPlatform1.getPluid()));
         departmentVO.setId(deptForPlatform1.getPlnum());
         departmentVO.setName(deptForPlatform1.getPlname());
         departmentVO.setStatus(deptForPlatform1.getPlstatus());
@@ -552,7 +588,7 @@
   }
   /**
    * 保存部门角色关联信息,带查重功能
    * 保存部门用户关联信息,带查重功能
    * @param userOIds 用户id
    * @param deptId 部门id
    * @return
@@ -602,7 +638,7 @@
      if(Func.isNotBlank(orgDepartmentDTO.getId())){
         conditionMap.put("plnum",QueryOptionConstant.OR + orgDepartmentDTO.getId());
      }
      List<OrgDepartmentVO> departmentVOS = getDeptByDeptPOidAndCondition(orgDepartmentDTO.getPkFatherDepartment(), conditionMap);
      List<OrgDepartmentVO> departmentVOS = getDeptByDeptpOidAndCondition(orgDepartmentDTO.getPkFatherDepartment(), conditionMap);
      if(Func.isNotEmpty(departmentVOS)){
         throw new VciBaseException("同一父节点下该部门名称或编号已经存在,请修改!");
      }
@@ -647,7 +683,7 @@
      if(Func.isNotBlank(orgDepartmentDTO.getId())){
         conditionMap.put("plnum",QueryOptionConstant.OR + orgDepartmentDTO.getId());
      }
      List<OrgDepartmentVO> repeatDepartmentVOS = getDeptByDeptPOidAndCondition(orgDepartmentDTO.getPkFatherDepartment(), conditionMap);
      List<OrgDepartmentVO> repeatDepartmentVOS = getDeptByDeptpOidAndCondition(orgDepartmentDTO.getPkFatherDepartment(), conditionMap);
      repeatDepartmentVOS = repeatDepartmentVOS.stream().filter(item -> {
         if((item.getName().equals(orgDepartmentDTO.getName()) || item.getId().equals(orgDepartmentDTO.getId())) &&
               !item.getOid().equals(orgDepartmentDTO.getOid())){
@@ -707,19 +743,8 @@
      //TODO: 应该是不具备连带删除的功能,部门删除后用户关联的无用部门还在,考虑后期是否需要做,数据量不大可以不做连带删除
      return platformClientUtil.getFrameworkService().deleteDepartment(
            ids,
            new UserEntityInfo("developer"/*WebThreadLocalUtil.getCurrentUserSessionInfoInThread().getUserId()*/, null)
            new UserEntityInfo(WebThreadLocalUtil.getCurrentUserSessionInfoInThread().getUserId(), null)
      );
   }
   /**
    * 获取所有部门的信息
    * @return key:部门由名称组成的路径(/间隔),value对应最小层级的部门信息
    */
   @Override
   public Map<String, OrgDepartmentVO> getDeptAllTreeMap() {
      List<OrgDepartmentVO> orgDepartmentVOList = listAllLevelChildrenDeptByParentOid(null, null);
      Map<String, OrgDepartmentVO> stringOrgDepartmentVOMap = convertToMap(orgDepartmentVOList);
      return stringOrgDepartmentVOMap;
   }
   /**
@@ -732,10 +757,10 @@
      //界面没传名称,使用默认名称
      downloadFileName = Func.isBlank(downloadFileName) ?  "部门导入模板_" + Func.format(new Date(),"yyyy-MM-dd HHmmss.sss"):downloadFileName;
      // 设置表单列名
      List<String> columns = new ArrayList<>(Arrays.asList("ID", "名称", "编号", "代号", "专业", "父ID(部门唯一标识ID)", "描述"));
      List<String> columns = new ArrayList<>(Arrays.asList("名称", "编号", "代号", "专业", "父部门名称全路径(/间隔)", "描述"));
      //设置必填列
      ColumnNameisRed.clear();
      //ColumnNameisRed.add(0);
      ColumnNameisRed.add(0);
      ColumnNameisRed.add(1);
      //写excel
      String excelPath = LocalFileUtil.getDefaultTempFolder() + File.separator + downloadFileName +  ".xls";
@@ -785,53 +810,56 @@
         if(CollectionUtils.isEmpty(poList)){
            return BaseResult.fail(ExcelLangCodeConstant.IMPORT_CONTENT_NULL,new String[]{});
         }
         /*部门导入几个比较重要的情景:
            1、当前导入的数据是一个部门树结构(要判断用户手输的ID是否存在重复,输了就要判断是否在当前表格中重复,
               并且保存时不能使用用户手输的ID,要根据手输ID和实际存储OID做对应映射,
               并且不破坏表格中部门树结构,如果没手输ID就只需要关注parentId这个属性,
               并且需要注意如果有的ID有,有的没有那就需要注意,既存在和系统中,
               关联的部门树,又存在和当前表格中关联的部门树)。
            2、当前导入的数据父id关联了已存在的部门oid(要查询父id这个部门是否存在,)
         解决思路:最好是将表格中可能是部门树的给遍历成树,然后再做查重啥的处理
         */
         //2、必填判空、判重(数据库判重和excel中判重),组装成保存用的数据对象
         List<DeptInfo> deptInfoList = new ArrayList<>();
         //2.1、用以存储excel中重复的数据,三个不可重复的字段)(Name不能为空,Name、Code同一父部门下唯一)
         List<String> repeatIdList = new ArrayList<>();
         //2.1、用以存储excel中重复的数据,两个不可重复的字段)(Name不能为空,Name、Code同一父部门下唯一)
         Map<String,String> repeatNameMap = new HashMap<>();
         Map<String,String> repeatNumMap = new HashMap<>();
         Map<String,String> indexMap = new HashMap<>();
         //2.2、存储用户手输的oid和实际存储oid的映射关系
         Map<String, String> oidMap = new HashMap<>();
         //查询系统中部门,其中包含了部门名称树属性
         List<OrgDepartmentVO> dbOrgDepartmentVOList = this.getDeptAllFullName();
         //遍历成map对象:key为DeptName的全路径:value为Dept对象(用户处理部门父组件)
         Map<String, String> dbOrgDepartFullNameOidMap = dbOrgDepartmentVOList.stream().collect(Collectors.toMap(OrgDepartmentVO::getFullDeptNamePath, OrgDepartmentVO::getOid));
         //用来判断处parentName既不在库中存在,又不在poNames存在的情况
         List<String> poNames = poList.stream().map(OrgDeptPO::getName).collect(Collectors.toList());
         poList.stream().forEach(po->{
            String parentId = Func.isBlank(po.getParentId()) ? "":po.getParentId();
            String parentFullNamePath = Func.isBlank(po.getParentFullNamePath()) ? "":po.getParentFullNamePath();
            //部门名称判空,通常通过po中的注解就可实现
            if(Func.isEmpty(po.getName())){
            if(Func.isBlank(po.getName())){
               throw new VciBaseException("第【"+po.getRowIndex()+"】行,depterror,Reason:Name cannot be empty");
            }else if(parentId.equals(repeatNameMap.getOrDefault(po.getName(), null))/*excel中同一部门下Name相等*/){
            }else if(parentFullNamePath.equals(repeatNameMap.getOrDefault(po.getName(), null))/*excel中同一部门下Name相等*/){
               //同一部门下名称判重
               throw new VciBaseException("第【"+po.getRowIndex()+"】行,deptnameerror,Reason: Names under the same department cannot be duplicated");
            }else if(Func.isNotEmpty(po.getId()) && repeatIdList.equals(po.getId())){
               throw new VciBaseException("第【"+po.getRowIndex()+"】行,deptiderror,Reason: The primary key cannot be duplicated");
            }else if(Func.isNotEmpty(po.getNum()) && parentId.equals(repeatNumMap.getOrDefault(po.getNum(),null))/*excel中同一部门下编号存在 */){
            }else if(Func.isNotBlank(po.getNum()) && parentFullNamePath.equals(repeatNumMap.getOrDefault(po.getNum(),null))/*excel中同一部门下编号存在 */){
               throw new VciBaseException("第【"+po.getRowIndex()+"】行,deptnumerror,Reason: The number cannot be duplicated");
            }else{
               //2.2、查询数据库中的数据(查重ID和NUM),比较麻烦需要根据ParentID查询(所有只能单条查询进行判重)
               //组装查重条件:同一部门下(parentId相等),name或者num相等
               HashMap<String, String> conditionMap = new HashMap<>();
               conditionMap.put("plname",QueryOptionConstant.OR + po.getName());
               conditionMap.put("plnum",QueryOptionConstant.OR + po.getNum());
               String pId = Func.isBlank(po.getParentId()) ? null:po.getParentId();
               List<OrgDepartmentVO> repeatOrgDept = this.getDeptByDeptPOidAndCondition(pId, conditionMap);
               //2.2、查询数据库中的数据(查重ID和NUM),比较麻烦需要根据ParentName全路径查询(所有只能单条查询进行判重)
               //同一部门下(parentName相等),name或者num相等
               List<OrgDepartmentVO> repeatOrgDept = dbOrgDepartmentVOList.stream().filter(item -> {
                  //同一部门名称全路径下,部门名称相等、部门编号不为空并且和系统中存在相等的编号
                  boolean isNameOrNumRepeat = po.getName().equals(item.getName()) || (Func.isNotBlank(po.getNum()) && po.getNum().equals(item.getId()));
                  /*当parentFullNamePath为""时item.getFullDeptNamePath().contains(parentFullNamePath)永远为true,
                  所以需要特殊处理直接判断顶层的部门是否存在重复*/
                  if(((Func.isBlank(parentFullNamePath) && item.getTreelevel() == 1)
                     || (Func.isNotBlank(parentFullNamePath) && item.getFullDeptNamePath().contains(parentFullNamePath)))
                     && isNameOrNumRepeat) {
                     return true;
                  }
                  return false;
               }).collect(Collectors.toList());
               //只要不为空就说明当前行数据在系统中重复
               if(Func.isNotEmpty(repeatOrgDept)){
                  throw new VciBaseException("第【"+po.getRowIndex()+"】行,deptname or deptnum error,Reason: The name or number already exists in the system");
               }
            }
            //存储校验通过的数据,以便后续excel查重
            repeatNameMap.put(po.getName(),Func.isBlank(po.getParentId()) ? "":po.getParentId());
            repeatIdList.add(po.getId());
            repeatNumMap.put(po.getNum(),po.getParentId());
            repeatNameMap.put(po.getName(),parentFullNamePath);
            if(Func.isNotBlank(po.getNum())){
               repeatNumMap.put(po.getNum(),parentFullNamePath);
            }
            indexMap.put(po.getName(),po.getRowIndex());
            //校验数据就该组装成DTO数据对象了
            OrgDepartmentDTO dto = new OrgDepartmentDTO();
@@ -840,48 +868,51 @@
            dto.setId(po.getNum());
            dto.setDescription(po.getDesc());
            dto.setSpecialties(po.getSpecialties());
            dto.setPkFatherDepartment(po.getParentId());
            dto.setCreateTime(new Date());
            dto.setCreator(loginUserId);
            dto.setLastModifier(loginUserId);
            dto.setStatus((short) 0);
            //2.2、过程中处理主键转换问题最好是一条一条的处理ID是否存在还有ParentID是否存在的问题
            //先判断是已存在的部门ID,还是不存在的部门ID,已存在系统中的ID就是为Dept:开头的
            String pId = Func.isBlank(dto.getPkFatherDepartment()) ? "":dto.getPkFatherDepartment();
            //是已存在系统中的部门id
            if(pId.contains("Dept:")){
               //解析出真实的oid
               String deParentId = Func.oidDeHash(pId.replace("Dept:", "").trim());
               //解析出来的deParentId必须得在库中存在
               if(Func.isBlank(deParentId) || Func.isEmpty(this.getDeptByDeptOid(deParentId))){
                  throw new VciBaseException("第【"+ po.getRowIndex() +"】行,deptparentiderror,Reason:Parent ID resolution error or does not exist in the system");
            //给导入的数据设置的主键
            String oid = VciBaseUtil.getPk().toUpperCase(Locale.ROOT);
            dto.setOid(oid);
            /*处理部门名称全路径转换为PkFatherDepartment(部门主键):
              情况1、直属父部门是系统中已存在(判断方式:部门名称全路径在dbOrgDepartmentVOList中存在,fullDeptNamePath全等于parentFullNamePath)。
              情况2、直属父部门不是系统中已存在的,但是直属父部门的的上级部门是系统中已存在的(涉及到oid和parentoid对应关系处理比较麻烦)。
              情况3、直属父部门和其上级部门都是excel中新构建的。(涉及到oid和parentoid对应关系处理比较麻烦)*/
            String dbDeptoid = dbOrgDepartFullNameOidMap.getOrDefault(parentFullNamePath, null);
            //情况1可以直接设置parentOid。
            if(Func.isNotEmpty(dbDeptoid) || "".equals(parentFullNamePath)){
               //设置父部门主键
               dto.setPkFatherDepartment(dbDeptoid);
            }else{
               //处理父路径名既不存在于数据库又不存在于当前excel
               String lastParentFullName = parentFullNamePath.substring(parentFullNamePath.lastIndexOf("/") + 1);//父路径名的最后一个部门名称
               if(Func.isBlank(dbOrgDepartFullNameOidMap.getOrDefault(parentFullNamePath,null))
                  && !poNames.contains(lastParentFullName)
               ){
                  throw new VciBaseException("当前导入的部门数据中,第【" + indexMap.get(po.getName()) + "】行,父部门设置存在问题!");
               }
               //替换掉临时使用的部门唯一标识,但是为了方便后续判断,还是需要加上Dept:标识
               dto.setPkFatherDepartment("Dept:"+deParentId);
               //情况2和3需要通过映射关系设置parentoid,所以这里先标记后续再做处理。
               dto.setPkFatherDepartment("Pending:" + parentFullNamePath);
            }
            //用户手输了部门oid,但是避免oid不规范需要,用规范oid进行替换,如果没输入保存时会自动生成oid
            String excelOid = dto.getOid();
            if(Func.isNotBlank(excelOid)){
               String pkOid = VciBaseUtil.getPk().toUpperCase(Locale.ROOT);
               dto.setOid(pkOid);
               //oid映射关系,后续用来更新parentId
               oidMap.put(excelOid,pkOid);
            }
            //存储parentOid:oid映射关系
            String key = Func.isBlank(parentFullNamePath) ? po.getName():parentFullNamePath + "/"+po.getName();
            oidMap.put(key,oid);
            DeptInfo deptInfo = this.changeOrgDeptDTOToDeptInfo(dto);
            deptInfoList.add(deptInfo);
         });
         //3、处理id和parentId的映射关系:将用户手输的父ID转换成实际存储的ID并保存进库
         //3、处理oid和parentOid的映射关系:针对新加的数据是父部门
         deptInfoList.stream().forEach(info -> {
            //3.1、对最终处理好的部门数据保存
            try {
               //parentId不为空并且没有Dept:相关的字符串,说明是用户手输的ParentId(导入的数据就是一个树结构)
               if (Func.isNotBlank(info.parentId) && !info.parentId.contains("Dept:")) {
                  //转换ParentId为实际要存储的部门ID
                  info.parentId = oidMap.get(info.parentId);
               }else if(info.parentId.contains("Dept:")){
               //parentId不为空并且没有Pending:相关的字符串,说明是需要处理oid映射parentOid的
               if (info.parentId.contains("Pending:")){
                  //系统中已存在的父部门OID,需要移除掉Dept:标识
                  info.parentId = info.parentId.replace("Dept:","");
                  String key = info.parentId.replace("Pending:","");
                  String parentId = oidMap.get(key);
                  info.parentId = parentId;
               }
               //4、保存操作
               platformClientUtil.getFrameworkService().saveDepartment(
                     info,
                     userEntityInfo
@@ -901,7 +932,23 @@
      return BaseResult.success("部门导入成功!");
   }
   public Map<String, OrgDepartmentVO> convertToMap(List<OrgDepartmentVO> orgDepartmentVOList) {
   /**
    * 获取所有部门的信息
    * @return key:部门由名称组成的路径(/间隔),value对应最小层级的部门信息
    */
   @Override
   public Map<String, OrgDepartmentVO> getDeptAllTreeMap() {
      List<OrgDepartmentVO> orgDepartmentVOList = listAllLevelChildrenDeptByParentOid(null, null);
      Map<String, OrgDepartmentVO> stringOrgDepartmentVOMap = convertToMap(orgDepartmentVOList);
      return stringOrgDepartmentVOMap;
   }
   /**
    * 获取部门由名称组成的路径(/间隔),value对应最小层级的部门信息
    * @param orgDepartmentVOList
    * @return
    */
   private Map<String, OrgDepartmentVO> convertToMap(List<OrgDepartmentVO> orgDepartmentVOList) {
      Map<String, OrgDepartmentVO> map = new HashMap<>();
      for (OrgDepartmentVO orgDepartmentVO : orgDepartmentVOList) {
         String key = buildKey(orgDepartmentVO, orgDepartmentVOList);