TREESELECT_EXECUTION_FLOW.md 6.9 KB

/system/menu/treeselect 接口执行流程详解

完整执行顺序

1. 控制器层 - SysMenuController.treeselect()

位置: SysMenuController.java:84-89

@SaCheckPermission("system:menu:query")  // ← 步骤1: 权限校验
@GetMapping("/treeselect")
public R<List<Tree<Long>>> treeselect(SysMenuBo menu) {
    // 步骤2: 查询菜单列表
    List<SysMenuVo> menus = menuService.selectMenuList(menu, LoginHelper.getUserId());
    
    // 步骤3: 构建树形结构
    return R.ok(menuService.buildMenuTreeSelect(menus));
}

执行步骤:

  1. 权限校验: @SaCheckPermission("system:menu:query") - 检查用户是否有 system:menu:query 权限
  2. 获取用户ID: LoginHelper.getUserId() - 从当前登录上下文获取用户ID
  3. 调用服务层: menuService.selectMenuList(menu, userId)
  4. 构建树结构: menuService.buildMenuTreeSelect(menus)
  5. 返回结果: 包装成 R<List<Tree<Long>>> 返回

2. 服务层 - SysMenuServiceImpl.selectMenuList()

位置: SysMenuServiceImpl.java:73-93

public List<SysMenuVo> selectMenuList(SysMenuBo menu, Long userId) {
    List<SysMenuVo> menuList;
    LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
    
    // ========== 步骤A: 权限过滤 ==========
    if (!LoginHelper.isSuperAdmin(userId)) {
        // 非超管用户,需要通过角色过滤菜单
        wrapper.inSql(SysMenu::getMenuId, baseMapper.buildMenuByUserSql(userId));
    }
    
    // ========== 步骤B: 平台过滤 ==========
    int platformId = menu.getPlatformId()==null? PlatformUtils.getId() : menu.getPlatformId();
    
    // ========== 步骤C: 构建查询条件并执行 ==========
    menuList = baseMapper.selectVoList(
        wrapper.like(StringUtils.isNotBlank(menu.getMenuName()), SysMenu::getMenuName, menu.getMenuName())
            .eq(StringUtils.isNotBlank(menu.getVisible()), SysMenu::getVisible, menu.getVisible())
            .eq(StringUtils.isNotBlank(menu.getStatus()), SysMenu::getStatus, menu.getStatus())
            .eq(SysMenu::getPlatformId, platformId)  // ← 关键:平台过滤
            .eq(StringUtils.isNotBlank(menu.getMenuType()), SysMenu::getMenuType, menu.getMenuType())
            .eq(ObjectUtil.isNotNull(menu.getParentId()), SysMenu::getParentId, menu.getParentId())
            .orderByAsc(SysMenu::getParentId)
            .orderByAsc(SysMenu::getOrderNum)
    );
    
    return menuList;
}

详细执行流程:

步骤A: 权限过滤(非超管用户)

if (!LoginHelper.isSuperAdmin(userId)) {
    wrapper.inSql(SysMenu::getMenuId, baseMapper.buildMenuByUserSql(userId));
}

生成的SQL子查询 (buildMenuByUserSql):

SELECT menu_id 
FROM sys_role_menu 
WHERE role_id IN (
    SELECT sur.role_id 
    FROM sys_user_role sur
    LEFT JOIN sys_role sr ON sr.role_id = sur.role_id
    WHERE sur.user_id = ? 
      AND sr.status = '0'
)

作用: 获取该用户所有角色分配的菜单ID列表

步骤B: 平台过滤

int platformId = PlatformUtils.getId();

执行流程:

  1. PlatformUtils.getId() 调用
  2. 从请求头获取 PLATFORM_CODE
  3. 根据 PLATFORM_CODE 转换为平台ID:
    • "PINGTAIDUAN" → 0 (平台端)
    • "SHANGHUDUAN" → 1 (商户端)

步骤C: 最终SQL查询

商户端用户的完整SQL:

SELECT * 
FROM sys_menu
WHERE menu_id IN (
    -- 用户角色分配的菜单ID列表
    SELECT menu_id FROM sys_role_menu WHERE role_id IN (...)
)
AND platform_id = 1  -- 商户端
ORDER BY parent_id, order_num

关键点: 这是一个 AND 条件,必须同时满足:

  • 菜单ID在用户角色分配的列表中
  • 菜单的 platform_id = 1

3. 数据层 - SysMenuMapper

位置: SysMenuMapper.java

使用 MyBatis-Plus 的 LambdaQueryWrapper 自动生成SQL并执行。


4. 树结构构建 - buildMenuTreeSelect()

位置: SysMenuServiceImpl.java:250-265

public List<Tree<Long>> buildMenuTreeSelect(List<SysMenuVo> menus) {
    if (CollUtil.isEmpty(menus)) {
        return CollUtil.newArrayList();  // ← 如果查询结果为空,返回空列表
    }
    return TreeBuildUtils.build(menus, (menu, tree) -> {
        tree.setId(menu.getMenuId())
            .setParentId(menu.getParentId())
            .setName(menu.getMenuName())
            .setWeight(menu.getOrderNum());
        // ... 设置其他属性
    });
}

为什么商户端查不到数据?

问题定位

根据执行流程,商户端查询的SQL实际上是:

SELECT * 
FROM sys_menu
WHERE menu_id IN (
    -- 步骤1: 获取用户角色分配的菜单
    SELECT menu_id FROM sys_role_menu 
    WHERE role_id IN (
        SELECT role_id FROM sys_user_role WHERE user_id = 2038434195944886273
    )
)
AND platform_id = 1  -- 步骤2: 过滤商户端菜单

问题原因

两个条件的交集为空:

  1. 步骤1结果: 用户角色分配的菜单(假设有 100 条)

    • 这些菜单的 platform_id 可能是 0、NULL 或其他值
  2. 步骤2过滤: platform_id = 1

    • 只保留商户端菜单
  3. 交集: 如果步骤1的100条菜单中,没有一条的 platform_id = 1

    • 最终结果为空

验证方法

执行以下SQL验证:

-- 查看用户角色分配的菜单的平台分布
SELECT sm.platform_id, COUNT(*) as count
FROM sys_user_role sur
LEFT JOIN sys_role_menu srm ON sur.role_id = srm.role_id
LEFT JOIN sys_menu sm ON srm.menu_id = sm.menu_id
WHERE sur.user_id = 2038434195944886273
GROUP BY sm.platform_id;

如果结果显示:

  • platform_id = 0: 100 条
  • platform_id = 1: 0 条

说明: 用户角色分配的菜单都是平台端的,没有商户端的,所以查询结果为空。


解决方案

方案1: 修改菜单数据(推荐)

将用户角色需要的菜单改为 platform_id = 1:

UPDATE sys_menu SET platform_id = 1 
WHERE menu_id IN (
    SELECT menu_id FROM sys_role_menu 
    WHERE role_id IN (
        SELECT role_id FROM sys_user_role 
        WHERE user_id = 2038434195944886273
    )
);

方案2: 重新分配角色菜单

删除旧的菜单权限,分配新的 platform_id = 1 的菜单:

-- 1. 获取角色ID
SELECT role_id FROM sys_user_role WHERE user_id = 2038434195944886273;

-- 2. 删除旧权限
DELETE FROM sys_role_menu WHERE role_id = ?;

-- 3. 分配新权限
INSERT INTO sys_role_menu (role_id, menu_id)
SELECT ?, menu_id FROM sys_menu WHERE platform_id = 1;

调试建议

在以下位置添加日志:

  1. SysMenuServiceImpl.java:82 - 查看 platformId 的值
  2. SysMenuServiceImpl.java:92 - 查看 menuList.size()
  3. PlatformUtils.java:8 - 查看请求头中的 PLATFORM_CODE

    log.info("查询菜单 - userId={}, platformId={}, isSuperAdmin={}", 
    userId, platformId, LoginHelper.isSuperAdmin(userId));
    log.info("查询结果 - menuList.size()={}", menuList.size());