Przeglądaj źródła

人员权限一览表 查询接口

ZhuJiaHao 1 miesiąc temu
rodzic
commit
6e9e6a2846

+ 81 - 0
ibps-model-root/modules/org-model/src/main/java/com/lc/ibps/org/party/persistence/entity/ResourceDO.java

@@ -0,0 +1,81 @@
+package com.lc.ibps.org.party.persistence.entity;
+
+public class ResourceDO {
+
+    private String id;
+    private String name;
+    private Integer sn;
+    private String parentId;
+    private String path;
+    private String displayName;
+    private String sortPath;
+
+    public ResourceDO(String id, String name, Integer sn, String parentId, String path, String displayName, String sortPath) {
+        this.id = id;
+        this.name = name;
+        this.sn = sn;
+        this.parentId = parentId;
+        this.path = path;
+        this.displayName = displayName;
+        this.sortPath = sortPath;
+    }
+
+    public ResourceDO() {
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getSn() {
+        return sn;
+    }
+
+    public void setSn(Integer sn) {
+        this.sn = sn;
+    }
+
+    public String getParentId() {
+        return parentId;
+    }
+
+    public void setParentId(String parentId) {
+        this.parentId = parentId;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
+    public String getSortPath() {
+        return sortPath;
+    }
+
+    public void setSortPath(String sortPath) {
+        this.sortPath = sortPath;
+    }
+}

+ 71 - 0
ibps-model-root/modules/org-model/src/main/java/com/lc/ibps/org/party/persistence/entity/ResourceRawDO.java

@@ -0,0 +1,71 @@
+package com.lc.ibps.org.party.persistence.entity;
+
+public class ResourceRawDO {
+
+    private String id;
+    private String name;
+    private Integer sn;
+    private String parentId;
+    private String path;
+    private String displayInMenu;
+
+    public ResourceRawDO(String id, String name, Integer sn, String parentId, String path, String displayInMenu) {
+        this.id = id;
+        this.name = name;
+        this.sn = sn;
+        this.parentId = parentId;
+        this.path = path;
+        this.displayInMenu = displayInMenu;
+    }
+
+    public ResourceRawDO() {
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getSn() {
+        return sn;
+    }
+
+    public void setSn(Integer sn) {
+        this.sn = sn;
+    }
+
+    public String getParentId() {
+        return parentId;
+    }
+
+    public void setParentId(String parentId) {
+        this.parentId = parentId;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public String getDisplayInMenu() {
+        return displayInMenu;
+    }
+
+    public void setDisplayInMenu(String displayInMenu) {
+        this.displayInMenu = displayInMenu;
+    }
+}

+ 30 - 0
ibps-model-root/modules/org-model/src/main/java/com/lc/ibps/org/party/persistence/entity/RoleDO.java

@@ -0,0 +1,30 @@
+package com.lc.ibps.org.party.persistence.entity;
+
+public class RoleDO {
+    private String id;
+    private String name;
+
+    public RoleDO(String id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    public RoleDO() {
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}

+ 30 - 0
ibps-model-root/modules/org-model/src/main/java/com/lc/ibps/org/party/persistence/entity/RoleResourceDO.java

@@ -0,0 +1,30 @@
+package com.lc.ibps.org.party.persistence.entity;
+
+public class RoleResourceDO {
+    private String roleId;
+    private String resId;
+
+    public RoleResourceDO(String roleId, String resId) {
+        this.roleId = roleId;
+        this.resId = resId;
+    }
+
+    public RoleResourceDO() {
+    }
+
+    public String getResId() {
+        return resId;
+    }
+
+    public void setResId(String resId) {
+        this.resId = resId;
+    }
+
+    public String getRoleId() {
+        return roleId;
+    }
+
+    public void setRoleId(String roleId) {
+        this.roleId = roleId;
+    }
+}

+ 43 - 0
ibps-model-root/modules/org-model/src/main/java/com/lc/ibps/org/party/persistence/entity/UserDO.java

@@ -0,0 +1,43 @@
+package com.lc.ibps.org.party.persistence.entity;
+
+public class UserDO {
+
+    private String id;
+    private String name;
+    private String job;
+
+    public UserDO(String id, String name, String job) {
+        this.id = id;
+        this.name = name;
+        this.job = job;
+    }
+
+    public UserDO() {
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getJob() {
+        return job;
+    }
+
+    public void setJob(String job) {
+        this.job = job;
+    }
+
+    // JOB_(逗号分隔的角色ID)
+}

+ 133 - 0
ibps-model-root/modules/org-model/src/main/resources/com/lc/ibps/org/party/persistence/mapping/PartyRoleProvider.map.xml

@@ -119,4 +119,137 @@
             (select SYSTEM_ID_ from IBPS_AUTH_ROLE_SYSTEM where ROLE_ID_ = (select ID_ from IBPS_PARTY_ROLE where NAME_ = #{roleName} LIMIT 1))
         )
     </insert>
+
+
+
+    <!-- RoleDO 结果映射 -->
+    <resultMap id="RoleDOMap" type="com.lc.ibps.org.party.persistence.entity.RoleDO">
+        <id property="id" column="ID_" jdbcType="VARCHAR"/>
+        <result property="name" column="NAME_" jdbcType="VARCHAR"/>
+    </resultMap>
+
+    <!-- 查询所有角色 -->
+    <select id="getAllRoles" resultMap="RoleDOMap">
+        SELECT
+        ID_,
+        NAME_
+        FROM IBPS_PARTY_ROLE
+        ORDER BY NAME_
+    </select>
+
+    <!-- 查询有效资源并构建显示名称和排序路径,同时过滤父级不显示的菜单 -->
+    <select id="getResourcesWithPathPaging" resultType="com.lc.ibps.org.party.persistence.entity.ResourceDO">
+        SELECT
+        r.ID_          AS id,
+        r.NAME_        AS name,
+        r.SN_          AS sn,
+        r.PARENT_ID_   AS parentId,
+        r.PATH_        AS path,
+        (
+        SELECT GROUP_CONCAT(parent.NAME_ ORDER BY FIND_IN_SET(parent.ID_, REPLACE(r.PATH_, '.', ',')) SEPARATOR '/')
+        FROM IBPS_AUTH_RES parent
+        WHERE FIND_IN_SET(parent.ID_, REPLACE(r.PATH_, '.', ','))
+        ) AS displayName,
+        (
+        SELECT GROUP_CONCAT(LPAD(parent.SN_, 10, '0') ORDER BY FIND_IN_SET(parent.ID_, REPLACE(r.PATH_, '.', ',')) SEPARATOR '.')
+        FROM IBPS_AUTH_RES parent
+        WHERE FIND_IN_SET(parent.ID_, REPLACE(r.PATH_, '.', ','))
+        ) AS sortPath
+        FROM IBPS_AUTH_RES r
+        WHERE r.DISPLAY_IN_MENU_ = 'Y'
+        AND NOT EXISTS (
+        SELECT 1
+        FROM (
+        SELECT
+        SUBSTRING_INDEX(SUBSTRING_INDEX(r.PATH_, '.', numbers.n), '.', -1) AS pid
+        FROM (
+        SELECT 1 n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5
+        UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10
+        ) numbers
+        WHERE numbers.n <![CDATA[ <= ]]> (LENGTH(r.PATH_) - LENGTH(REPLACE(r.PATH_, '.', '')) + 1)
+        ) path_ids
+        LEFT JOIN IBPS_AUTH_RES parent_res ON parent_res.ID_ = path_ids.pid
+        WHERE parent_res.ID_ IS NULL OR parent_res.DISPLAY_IN_MENU_ != 'Y'
+        )
+        <if test="resourceNameFilter != null and resourceNameFilter != ''">
+            AND displayName LIKE CONCAT('%', #{resourceNameFilter}, '%')
+        </if>
+        ORDER BY sortPath
+        LIMIT #{offset}, #{limit}
+    </select>
+
+    <!-- 统计有效资源总数 -->
+    <select id="countResources" resultType="int">
+        SELECT COUNT(*)
+        FROM (
+        SELECT
+        r.ID_,
+        (
+        SELECT GROUP_CONCAT(parent.NAME_ ORDER BY FIND_IN_SET(parent.ID_, REPLACE(r.PATH_, '.', ',')) SEPARATOR '/')
+        FROM IBPS_AUTH_RES parent
+        WHERE FIND_IN_SET(parent.ID_, REPLACE(r.PATH_, '.', ','))
+        ) AS displayName
+        FROM IBPS_AUTH_RES r
+        WHERE r.DISPLAY_IN_MENU_ = 'Y'
+        AND NOT EXISTS (
+        SELECT 1
+        FROM (
+        SELECT
+        SUBSTRING_INDEX(SUBSTRING_INDEX(r.PATH_, '.', numbers.n), '.', -1) AS pid
+        FROM (
+        SELECT 1 n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5
+        UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10
+        ) numbers
+        WHERE numbers.n <![CDATA[ <= ]]> (LENGTH(r.PATH_) - LENGTH(REPLACE(r.PATH_, '.', '')) + 1)
+        ) path_ids
+        LEFT JOIN IBPS_AUTH_RES parent_res ON parent_res.ID_ = path_ids.pid
+        WHERE parent_res.ID_ IS NULL OR parent_res.DISPLAY_IN_MENU_ != 'Y'
+        )
+        ) t
+        <if test="resourceNameFilter != null and resourceNameFilter != ''">
+            WHERE t.displayName LIKE CONCAT('%', #{resourceNameFilter}, '%')
+        </if>
+    </select>
+
+    <!-- 查询有效的角色-资源权限关系 -->
+    <select id="getValidRoleResources" resultType="com.lc.ibps.org.party.persistence.entity.RoleResourceDO">
+        SELECT DISTINCT
+        rr.ROLE_ID_ AS roleId,
+        rr.RES_ID_  AS resId
+        FROM IBPS_AUTH_ROLE_RES rr
+        WHERE EXISTS (
+        SELECT 1 FROM IBPS_PARTY_ROLE r WHERE r.ID_ = rr.ROLE_ID_
+        )
+        AND EXISTS (
+        SELECT 1 FROM IBPS_AUTH_RES r WHERE r.ID_ = rr.RES_ID_ AND r.DISPLAY_IN_MENU_ = 'Y'
+        )
+    </select>
+
+
+    <select id="getAllDisplayableResources" resultType="com.lc.ibps.org.party.persistence.entity.ResourceRawDO">
+        SELECT
+        ID_             AS id,
+        NAME_           AS name,
+        SN_             AS sn,
+        PARENT_ID_      AS parentId,
+        PATH_           AS path,
+        DISPLAY_IN_MENU_ AS displayInMenu
+        FROM IBPS_AUTH_RES
+        WHERE DISPLAY_IN_MENU_ = 'Y'
+    </select>
+
+
+    <select id="getAllUsers" resultType="com.lc.ibps.org.party.persistence.entity.UserDO">
+        SELECT
+        ID_   AS id,
+        NAME_ AS name,
+        JOB_  AS job
+        FROM IBPS_PARTY_EMPLOYEE
+        WHERE STATUS_ != 'deleted'
+        <if test="userName != null and userName != ''">
+            AND NAME_ LIKE CONCAT('%', #{userName}, '%')
+        </if>
+        ORDER BY NAME_
+    </select>
+
 </mapper>

+ 45 - 0
ibps-provider-root/modules/provider-platform-default/src/main/java/com/lc/ibps/org/provider/PartyRoleProvider.java

@@ -894,6 +894,51 @@ public class PartyRoleProvider extends GenericProvider implements IPartyRoleServ
 	}
 
 
+
+
+	@Override
+	public APIResult<PermissionPageList<PermissionViewDTO>> getPeoplePermission(@RequestBody(required = true) APIRequest request) {
+		/*
+		返回前端渲染用的json格式:
+		{
+		  "roles": [
+			"角色a",
+			"角色b",
+			"角色c",
+			"角色d",
+			"角色e",
+		  ],
+		  "permissions": [
+			{
+			  "pageId": 1,
+			  "pageName": "页面1",
+			  "角色a": false,
+			  "角色b": true,
+			  "角色c": true,
+			  "角色d": true,
+			  "角色e": false
+			},
+			{
+			  "pageId": 2,
+			  "pageName": "页面2",
+			  "角色a": false,
+			  "角色b": true,
+			  "角色c": true,
+			  "角色d": true,
+			  "角色e": false
+			},
+			......多个对象省略
+		  ]
+		}
+		 */
+
+		APIResult<PermissionPageList<PermissionViewDTO>> result = partyRoleProviderService.getPeoplePermission(request);
+
+		return result;
+	}
+
+
+
 	@ApiOperation(value = "查询角色树", notes = "查询角色树")
 	@Override
 	public APIResult<List<PartyRoleTreePo>> findTreeData(

+ 38 - 1
ibps-provider-root/modules/provider-platform-default/src/main/java/com/lc/ibps/platform/dao/PartyRoleProviderDao.java

@@ -1,11 +1,12 @@
 package com.lc.ibps.platform.dao;
 
 import com.lc.ibps.org.auth.persistence.entity.ResourcesPo;
-import com.lc.ibps.org.party.persistence.entity.PartyRolePo;
+import com.lc.ibps.org.party.persistence.entity.*;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
+import java.util.Map;
 
 @Mapper
 public interface PartyRoleProviderDao {
@@ -37,4 +38,40 @@ public interface PartyRoleProviderDao {
      * 插入角色资源关联关系
      */
     int insertRoleResources(@Param("roleName")String roleName, @Param("resId")String resId);
+
+
+
+
+
+
+
+    /**
+     * 获取所有有效角色
+     */
+    List<RoleDO> getAllRoles();
+
+    /**
+     * 获取分页的有效页面资源,按排序路径排序,并过滤父级不显示的资源。
+     * @param offset 偏移量
+     * @param limit 每页数量
+     * @param resourceNameFilter 资源名称模糊过滤(可选)
+     */
+    List<ResourceDO> getResourcesWithPathPaging(@Param("offset") int offset,
+                                                @Param("limit") int limit,
+                                                @Param("resourceNameFilter") String resourceNameFilter);
+
+    /**
+     * 统计有效资源总数(用于分页)
+     * @param resourceNameFilter 资源名称模糊过滤(可选)
+     */
+    int countResources(@Param("resourceNameFilter") String resourceNameFilter);
+
+    /**
+     * 获取有效的角色-资源权限关系(排除垃圾数据)
+     */
+    List<RoleResourceDO> getValidRoleResources();
+
+    List<ResourceRawDO> getAllDisplayableResources();
+
+    List<UserDO> getAllUsers(@Param("userName") String userName);
 }

+ 249 - 5
ibps-provider-root/modules/provider-platform-default/src/main/java/com/lc/ibps/platform/service/PartyRoleProviderService.java

@@ -8,15 +8,15 @@ import com.lc.ibps.cloud.entity.*;
 import com.lc.ibps.org.auth.persistence.entity.ResourcesPo;
 import com.lc.ibps.org.party.persistence.entity.*;
 import com.lc.ibps.platform.dao.PartyRoleProviderDao;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Optional;
+import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -30,6 +30,8 @@ public class PartyRoleProviderService {
 
     private ICommonDao<?> commonDao = AppUtil.getBean(ICommonDao.class);
 
+    protected static final Logger logger = LoggerFactory.getLogger(PartyRoleProviderService.class);
+
     /**
      * 增加用户角色
      * 仅限于更新IBPS_PARTY_EMPLOYEE表的JOB_字段内容
@@ -173,7 +175,6 @@ public class PartyRoleProviderService {
         apiPageList.setDataResult(null);
         apiResult.setData(apiPageList);
         apiResult.setMessage("查询成功");
-
         return apiResult;
     }
 
@@ -278,4 +279,247 @@ public class PartyRoleProviderService {
 
     }
 
+
+    /**
+     * 测试调用getRolePermissionMatrix方法
+     */
+    public void testGetRolePermissionMatrix(){
+        APIRequest request = new APIRequest();
+        Map<String, Object> customs = new HashMap<>();
+        //customs.put("pageName", "安全");
+
+        //customs.put("pageName", "风险评估计划");
+        //customs.put("peopleName", "李");
+
+        request.setCustoms(customs);
+        
+        APIRequestPage requestPage = new APIRequestPage();
+        requestPage.setPageNo(1);
+        requestPage.setLimit(50);
+        request.setRequestPage(requestPage);
+
+        APIResult<PermissionPageList<PermissionViewDTO>> response = getPeoplePermission(request);
+        System.out.println("------------------------------------------------------------");
+        System.out.println("------------------------------------------------------------");
+        System.out.println(response);
+
+    }
+
+
+
+
+    /**
+     * 人员权限一览表 查询(支持分页和模糊查询)
+     */
+    @Transactional(readOnly = true)
+    public APIResult<PermissionPageList<PermissionViewDTO>> getPeoplePermission(APIRequest request) {
+
+        String peopleName = (String) getCustomParamFormRequest(request, "peopleName");
+        String pageName = (String) getCustomParamFormRequest(request, "pageName");
+
+        int page = 1;
+        int limit = 50;
+        if (null != request.getRequestPage() && request.getRequestPage().getPageNo() > 0 && request.getRequestPage().getLimit() > 0) {
+            page = request.getRequestPage().getPageNo();
+            limit = request.getRequestPage().getLimit();
+        }
+
+        // 1. 获取所有有效用户(按名称排序),并根据 peopleName 过滤
+        List<UserDO> rawUsers = partyRoleProviderDao.getAllUsers(peopleName);
+        // 按名称排序
+        rawUsers.sort(Comparator.comparing(UserDO::getName));
+
+        // 去重:若存在同名用户,保留第一个并记录警告(确保 roles 列表与权限 Map 的 key 数量一致)
+        Map<String, UserDO> uniqueUserMap = new LinkedHashMap<>();
+        for (UserDO user : rawUsers) {
+            if (uniqueUserMap.containsKey(user.getName())) {
+                logger.warn("发现重复的用户名称: {}, 已忽略其中一个", user.getName());
+            } else {
+                uniqueUserMap.put(user.getName(), user);
+            }
+        }
+        List<UserDO> allUsers = new ArrayList<>(uniqueUserMap.values());
+        List<String> userNames = allUsers.stream().map(UserDO::getName).collect(Collectors.toList());
+
+        // 2. 获取所有原始资源(DISPLAY_IN_MENU_ = 'Y')
+        List<ResourceRawDO> rawResources = partyRoleProviderDao.getAllDisplayableResources();
+
+        // 3. 在内存中构建有效资源树,过滤父级不显示的,生成 displayName 和 sortPath
+        List<ResourceDO> validResources = buildValidResources(rawResources);
+
+        // 4. 应用资源名称过滤
+        if (StringUtils.isNotBlank(pageName)) {
+            validResources = validResources.stream()
+                    .filter(r -> r.getDisplayName().contains(pageName))
+                    .collect(Collectors.toList());
+        }
+
+        // 5. 总记录数(用于分页)
+        int totalCount = validResources.size();
+        int totalPages = (int) Math.ceil((double) totalCount / limit);
+        int offset = (page - 1) * limit;
+
+        // 6. 对资源进行分页
+        List<ResourceDO> pagedResources = validResources.stream()
+                .skip(offset)
+                .limit(limit)
+                .collect(Collectors.toList());
+
+        // 7. 获取角色-资源权限关系
+        List<RoleResourceDO> roleResources = partyRoleProviderDao.getValidRoleResources();
+        Map<String, Set<String>> roleResMap = new HashMap<>();
+        for (RoleResourceDO rr : roleResources) {
+            roleResMap.computeIfAbsent(rr.getRoleId(), k -> new HashSet<>()).add(rr.getResId());
+        }
+
+        // 8. 构建用户 -> 资源权限的映射(通过用户拥有的角色)
+        Map<String, Set<String>> userResMap = new HashMap<>();
+        for (UserDO user : allUsers) {
+            Set<String> userResIds = new HashSet<>();
+            if (StringUtils.isNotBlank(user.getJob())) {
+                String[] roleIds = user.getJob().split(",");
+                for (String roleId : roleIds) {
+                    Set<String> resSet = roleResMap.get(roleId);
+                    if (resSet != null) {
+                        userResIds.addAll(resSet);
+                    }
+                }
+            }
+            userResMap.put(user.getId(), userResIds);
+        }
+
+        // 9. 构建一个包含所有用户且默认值为 false 的 Map 模板
+        Map<String, Boolean> defaultUserPermissionMap = new LinkedHashMap<>();
+        for (UserDO user : allUsers) {
+            defaultUserPermissionMap.put(user.getName(), false);
+        }
+
+        // 10. 构建当前页的权限数据
+        List<PagePermissionDTO> permissionList = new ArrayList<>();
+        for (ResourceDO res : pagedResources) {
+            PagePermissionDTO dto = new PagePermissionDTO();
+            dto.setPageId(res.getId());
+            dto.setPageName(res.getDisplayName());
+
+            // 复制默认 map,然后为有权限的用户设置为 true
+            Map<String, Boolean> permissions = new LinkedHashMap<>(defaultUserPermissionMap);
+            for (UserDO user : allUsers) {
+                if (userResMap.getOrDefault(user.getId(), Collections.emptySet()).contains(res.getId())) {
+                    permissions.put(user.getName(), true);
+                }
+            }
+            dto.setRolePermissions(permissions);
+            permissionList.add(dto);
+        }
+
+        // 11. 构建返回对象
+        PermissionViewDTO viewDTO = new PermissionViewDTO(userNames, permissionList);
+        PermissionPageList<PermissionViewDTO> pageList = new PermissionPageList<>();
+        pageList.setData(viewDTO);
+        pageList.setPageResult(new APIPageResult(page, limit, totalCount));
+
+        APIResult<PermissionPageList<PermissionViewDTO>> result = new APIResult<>();
+        result.setState(200);
+        result.setMessage("查询成功");
+        result.setData(pageList);
+        return result;
+    }
+
+
+    /**
+     * 构建有效资源列表,过滤父级不显示的,并生成 displayName 和 sortPath
+     */
+    private List<ResourceDO> buildValidResources(List<ResourceRawDO> rawResources) {
+        // 构建 id -> ResourceRawDO 的映射
+        Map<String, ResourceRawDO> rawMap = rawResources.stream()
+                .collect(Collectors.toMap(ResourceRawDO::getId, r -> r));
+
+        // 过滤出所有父级都显示的资源 ID
+        Set<String> validIds = new HashSet<>();
+        for (ResourceRawDO res : rawResources) {
+            if (allParentsDisplay(res, rawMap)) {
+                validIds.add(res.getId());
+            }
+        }
+
+        // 对有效资源构建 displayName 和 sortPath
+        List<ResourceDO> result = new ArrayList<>();
+        for (ResourceRawDO res : rawResources) {
+            if (!validIds.contains(res.getId())) {
+                continue;
+            }
+            ResourceDO dto = new ResourceDO();
+            dto.setId(res.getId());
+            dto.setName(res.getName());
+            dto.setSn(res.getSn());
+            dto.setParentId(res.getParentId());
+            dto.setPath(res.getPath());
+            dto.setDisplayName(buildDisplayName(res, rawMap));
+            dto.setSortPath(buildSortPath(res, rawMap));
+            result.add(dto);
+        }
+
+        // 按 sortPath 排序
+        result.sort(Comparator.comparing(ResourceDO::getSortPath));
+        return result;
+    }
+
+
+    /**
+     * 检查资源的所有父级是否都显示
+     */
+    private boolean allParentsDisplay(ResourceRawDO res, Map<String, ResourceRawDO> rawMap) {
+        String path = res.getPath();
+        if (StringUtils.isBlank(path)) {
+            return true; // 根节点
+        }
+        String[] ids = path.split("\\.");
+        for (int i = 0; i < ids.length - 1; i++) { // 最后一个 ID 是自身,不检查
+            String pid = ids[i];
+            ResourceRawDO parent = rawMap.get(pid);
+            if (parent == null || !"Y".equals(parent.getDisplayInMenu())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 构建显示名称(如:设施环境管理/安全管理)
+     */
+    private String buildDisplayName(ResourceRawDO res, Map<String, ResourceRawDO> rawMap) {
+        String path = res.getPath();
+        if (StringUtils.isBlank(path)) {
+            return res.getName();
+        }
+        String[] ids = path.split("\\.");
+        List<String> names = new ArrayList<>();
+        for (String id : ids) {
+            ResourceRawDO node = rawMap.get(id);
+            if (node != null) {
+                names.add(node.getName());
+            }
+        }
+        return String.join("/", names);
+    }
+
+    /**
+     * 构建排序路径(各层 SN_ 左补零后拼接)
+     */
+    private String buildSortPath(ResourceRawDO res, Map<String, ResourceRawDO> rawMap) {
+        String path = res.getPath();
+        if (StringUtils.isBlank(path)) {
+            return String.format("%010d", res.getSn() != null ? res.getSn() : 0);
+        }
+        String[] ids = path.split("\\.");
+        List<String> snParts = new ArrayList<>();
+        for (String id : ids) {
+            ResourceRawDO node = rawMap.get(id);
+            if (node != null && node.getSn() != null) {
+                snParts.add(String.format("%010d", node.getSn()));
+            }
+        }
+        return String.join(".", snParts);
+    }
+
 }