You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Elixir递归实现组权限关联模块可见性过滤技术问询

Solution: Recursive Group Hierarchy Check for Module Visibility

Got it, let's break down how to implement this recursive check properly. The goal is to verify if the user is in the module's group or any of its parent groups (while ignoring deleted groups), and handle the case where the module has no group at all.

Step 1: Implement the Main Function

First, the main get_visible_module function will branch based on whether the module has an associated group. For ungrouped modules, you can decide visibility (I've assumed they're public here, but adjust as needed).

Step 2: Recursive Helper Function

We'll use a private recursive helper to traverse the group hierarchy. It checks if the user is in the current group, and if not, moves up to the parent group until there are no more parents or we find a match.

Full Code Implementation

def get_visible_module(module, user_id) do
  case module.group do
    # Handle modules with no associated group: adjust this return value based on your requirements
    nil -> true
    group -> user_has_access_to_group_hierarchy?(group, user_id)
  end
end

# Recursive helper to check group and all parent groups
defp user_has_access_to_group_hierarchy?(nil, _user_id), do: false

# Skip deleted groups entirely
defp user_has_access_to_group_hierarchy?(%{deleted: true}, _user_id), do: false

defp user_has_access_to_group_hierarchy?(group, user_id) do
  # First, run your existing basic check: is the user directly in this group?
  if user_in_group?(group.id, user_id) do
    true
  else
    # Fetch parent group if not preloaded (or use preloaded parent if available)
    parent_group = if is_nil(group.parent), do: Repo.get(Group, group.parent_id), else: group.parent
    # Recurse on the parent group
    user_has_access_to_group_hierarchy?(parent_group, user_id)
  end
end

# Your existing basic check (adjust if your implementation differs)
defp user_in_group?(group_id, user_id) do
  Repo.exists?(from ug in UserGroup, where: ug.group_id == ^group_id and ug.user_id == ^user_id)
end

Key Considerations

  1. Handling Deleted Groups: The helper skips any group marked as deleted: true—this ensures deleted groups don't grant access accidentally.
  2. Preloading vs. On-Demand Fetching: The code above fetches parent groups on-demand if they aren't preloaded. To avoid N+1 database queries (which can be slow for deep hierarchies), preload the entire group hierarchy when fetching the module. Here's how you could preload recursively:
    def preload_module_with_group_hierarchy(module_id) do
      module = Repo.get(Module, module_id) |> Repo.preload(:group)
      preload_parent_groups(module)
    end
    
    defp preload_parent_groups(%{group: nil} = module), do: module
    defp preload_parent_groups(%{group: group} = module) do
      updated_group = Repo.preload(group, :parent)
      preload_parent_groups(%{module | group: updated_group})
    end
    
  3. Ungrouped Modules: The nil case returns true—change this to false if ungrouped modules should be hidden by default.

Optimization Note

For very deep group hierarchies, a recursive Ecto query using Common Table Expressions (CTE) is more efficient than recursive function calls with individual DB queries. Here's a quick example of that approach:

def user_has_access_to_group_hierarchy?(group_id, user_id) do
  cte = """
    WITH RECURSIVE group_hierarchy AS (
      SELECT id, parent_id, deleted FROM groups WHERE id = ^group_id
      UNION ALL
      SELECT g.id, g.parent_id, g.deleted FROM groups g
      JOIN group_hierarchy gh ON g.id = gh.parent_id
    )
    SELECT EXISTS(
      SELECT 1 FROM user_groups ug
      JOIN group_hierarchy gh ON ug.group_id = gh.id
      WHERE ug.user_id = ^user_id AND gh.deleted = false
    )
  """
  Repo.query!(cte, [group_id, user_id]).rows |> hd() |> hd()
end

This query fetches all ancestor groups in one go and checks if the user is in any of them.


内容的提问来源于stack exchange,提问作者Sardoan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:25:16