perf: support UserPermTreeAPI finished.

This commit is contained in:
Bai
2025-12-26 18:04:42 +08:00
parent 447d3c13ac
commit ad1da2d23d
5 changed files with 160 additions and 106 deletions

View File

@@ -7,10 +7,13 @@ from assets.api.asset.asset import AssetFilterSet
from assets.models import Asset, Node, MyAsset
from common.api.mixin import ExtraFilterFieldsMixin
from common.utils import get_logger, lazyproperty, is_uuid
from common.utils.django import get_object_or_none
from orgs.utils import tmp_to_root_org
from perms import serializers
from perms.pagination import NodePermedAssetPagination, AllPermedAssetPagination
from perms.utils import UserPermAssetUtil, PermAssetDetailUtil
from perms.utils.utils import UserPermUtil
from perms.tree import PermTreeNode
from .mixin import (
SelfOrPKUserMixin
)
@@ -65,36 +68,46 @@ class BaseUserPermedAssetsApi(SelfOrPKUserMixin, ExtraFilterFieldsMixin, ListAPI
def get_assets(self):
return Asset.objects.none()
query_asset_util: UserPermAssetUtil
@lazyproperty
def query_asset_util(self):
return UserPermAssetUtil(self.user)
def _util(self):
return UserPermUtil(user=self.user)
class UserAllPermedAssetsApi(BaseUserPermedAssetsApi):
pagination_class = AllPermedAssetPagination
def get_assets(self):
if self.user.is_superuser and self.request.query_params.get('id'):
return Asset.objects.filter(id=self.request.query_params.get('id'))
node_id = self.request.query_params.get('node_id')
if is_uuid(node_id):
__, assets = self.query_asset_util.get_node_all_assets(node_id)
else:
assets = self.query_asset_util.get_all_assets()
if node_id == PermTreeNode.SpecialKey.FAVORITE:
return UserPermUtil.get_favorite_assets(user=self.user)
if node_id == PermTreeNode.SpecialKey.UNGROUPED:
return self._util.get_ungrouped_assets()
node = get_object_or_none(Node, id=node_id)
if node:
assets = self._util.get_node_all_assets(node)
return assets
assets = UserPermUtil.get_all_assets(user=self.user)
return assets
class UserDirectPermedAssetsApi(BaseUserPermedAssetsApi):
def get_assets(self):
return self.query_asset_util.get_direct_assets()
assets = self._util.get_ungrouped_assets()
return assets
class UserFavoriteAssetsApi(BaseUserPermedAssetsApi):
def get_assets(self):
return self.query_asset_util.get_favorite_assets()
assets = UserPermUtil.get_favorite_assets(user=self.user)
return assets
class UserPermedNodeAssetsApi(BaseUserPermedAssetsApi):

View File

@@ -27,8 +27,13 @@ class UserPermNodeChildrenAsTreeApi(SelfOrPKUserMixin, SerializeToTreeNodeMixin,
@timeit
def list(self, request, *args, **kwargs):
with_assets = request.query_params.get('assets', '0') == '1'
search = request.query_params.get('search')
key = request.query_params.get('key')
if not with_assets:
return self.init_user_perm_tree()
if search:
return self.search_user_perm_tree_with_assets(search)
@@ -36,6 +41,38 @@ class UserPermNodeChildrenAsTreeApi(SelfOrPKUserMixin, SerializeToTreeNodeMixin,
return self.expand_tree_node_with_assets(key)
return self.init_user_perm_tree_with_assets()
def init_user_perm_tree(self):
''' 初始化用户权限树 - 不包含资产
前端搜索
全局组织: 返回所有节点,不返回资产,不展开节点
实体组织返回所有节点不返回资产展开第1级节点
返回收藏节点
返回未分组节点 (如果需要)
'''
if current_org.is_root():
orgs = self.user.orgs.all()
expand_level = 0
else:
orgs = self.user.orgs.filter(id=current_org.id)
expand_level = 1
if not orgs.exists():
return Response(data=[])
nodes = []
for org in orgs:
tree = UserPermTree(user=self.user, org=org)
_nodes = tree.get_nodes()
nodes.extend(_nodes)
nodes = self.serialize_nodes(
nodes, with_asset_amount=True, expand_level=expand_level
)
data = nodes
data = self.add_favorites_and_ungrouped_node(data, with_assets=False)
return Response(data=data)
def init_user_perm_tree_with_assets(self):
''' 初始化用户权限资产树 - 包含资产
@@ -60,22 +97,6 @@ class UserPermNodeChildrenAsTreeApi(SelfOrPKUserMixin, SerializeToTreeNodeMixin,
if not orgs.exists():
return Response(data=[])
# 收藏节点和资产
f_node, f_assets = self.get_favorite_node_with_assets()
f_nodes = []
if f_node:
f_nodes = self.serialize_nodes(
[f_node], with_asset_amount=True, expand_level=0, with_assets=True
)
# 未分组节点和资产
u_node, u_assets = self.get_ungrouped_node_with_assets_if_need()
u_nodes = []
if u_node:
u_nodes = self.serialize_nodes(
[u_node], with_asset_amount=True, expand_level=0, with_assets=True
)
nodes = []
assets = []
@@ -91,10 +112,10 @@ class UserPermNodeChildrenAsTreeApi(SelfOrPKUserMixin, SerializeToTreeNodeMixin,
nodes = self.serialize_nodes(
nodes, with_asset_amount=True, expand_level=expand_level, with_assets=True
)
assets = [*f_assets, *u_assets, *assets]
assets = self.serialize_assets(assets)
data = [*f_nodes, *u_nodes, *nodes, *assets]
return Response(data)
data = [*nodes, *assets]
data = self.add_favorites_and_ungrouped_node(data, with_assets=True)
return Response(data=data)
def expand_tree_node_with_assets(self, key):
''' 展开用户权限资产树节点 - 包含资产
@@ -160,28 +181,48 @@ class UserPermNodeChildrenAsTreeApi(SelfOrPKUserMixin, SerializeToTreeNodeMixin,
assets = self.serialize_assets(assets)
data = [*nodes, *assets]
return Response(data=data)
def add_favorites_and_ungrouped_node(self, data: list, with_assets=False):
# 未分组节点和资产
u_node, u_assets = self.get_ungrouped_node_if_need()
if u_node:
data.insert(0, u_node)
data.extend(u_assets)
# 收藏节点和资产
f_node, f_assets = self.get_favorite_node()
if f_node:
data.insert(0, f_node)
data.extend(f_assets)
return data
def get_favorite_node_with_assets(self):
asset_ids = FavoriteAsset.get_user_favorite_asset_ids(self.user)
assets_amount = len(asset_ids)
def get_favorite_node(self, with_assets=False):
assets = UserPermUtil.get_favorite_assets(self.user)
assets_amount = assets.count()
node = PermTreeNode(
_id='favorite',
key='favorite',
value=_('Favorite'),
_id=PermTreeNode.SpecialKey.FAVORITE.value,
key=PermTreeNode.SpecialKey.FAVORITE.value,
value=PermTreeNode.SpecialKey.FAVORITE.label,
assets_amount=assets_amount
)
node.assets_amount_total = assets_amount
nodes = self.serialize_nodes(
[node], with_asset_amount=True, expand_level=0, with_assets=with_assets
)
serialized_node = nodes[0] if nodes else None
if not asset_ids:
return node, []
if not with_assets:
return serialized_node, []
if assets_amount == 0:
return serialized_node, []
assets = Asset.objects.filter(id__in=asset_ids).values(*AssetTreeNodeAsset.model_values)
assets = list(assets)
node.init_assets(assets)
assets = node.get_assets()
return node, assets
assets = list(assets.values(*AssetTreeNodeAsset.model_values))
assets = node.init_assets(assets)
serialized_assets = self.serialize_assets(assets)
return serialized_node, serialized_assets
def get_ungrouped_node_with_assets_if_need(self):
def get_ungrouped_node_if_need(self, with_assets=False):
if not settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
return None, []
@@ -193,18 +234,27 @@ class UserPermNodeChildrenAsTreeApi(SelfOrPKUserMixin, SerializeToTreeNodeMixin,
return None, []
_util = UserPermUtil(user=self.user, org=org)
direct_asset_ids = _util._user_direct_asset_ids
assets_amount = len(direct_asset_ids)
assets = _util.get_ungrouped_assets()
assets_amount = assets.count()
node = PermTreeNode(
_id='ungrouped',
key='ungrouped',
value=_('Ungrouped'),
_id=PermTreeNode.SpecialKey.UNGROUPED.value,
key=PermTreeNode.SpecialKey.UNGROUPED.value,
value=PermTreeNode.SpecialKey.UNGROUPED.label,
assets_amount=assets_amount
)
node.assets_amount_total = assets_amount
if not direct_asset_ids:
return node, []
assets = Asset.objects.filter(id__in=direct_asset_ids).values(*AssetTreeNodeAsset.model_values)
node.init_assets(list(assets))
assets = node.get_assets()
return node, assets
nodes = self.serialize_nodes(
[node], with_asset_amount=True, expand_level=0, with_assets=with_assets
)
serialized_node = nodes[0] if nodes else None
if not with_assets:
return serialized_node, []
if assets_amount == 0:
return serialized_node, []
assets = assets.values(*AssetTreeNodeAsset.model_values)
assets = node.init_assets(list(assets))
serialized_assets = self.serialize_assets(assets)
return serialized_node, serialized_assets

View File

@@ -25,6 +25,10 @@ class PermTreeNode(AssetTreeNode):
DN = 'dn'
# Node with only direct permission assets
DA = 'da'
class SpecialKey(TextChoices):
FAVORITE = 'favorite', _('Favorite')
UNGROUPED = 'ungrouped', _('Ungrouped')
def __init__(self, tp=None, **kwargs):
self.type = tp or self.Type.BRIDGE

View File

@@ -3,54 +3,11 @@ from django.urls import path, include
from .. import api
user_permission_urlpatterns = [
# <str:user> such as: my | self | user.id
# assets
path('<str:user>/assets/<uuid:pk>/', api.UserPermedAssetRetrieveApi.as_view(),
name='user-permed-asset'),
path('<str:user>/assets/', api.UserAllPermedAssetsApi.as_view(),
name='user-all-assets'),
path('<str:user>/nodes/ungrouped/assets/', api.UserDirectPermedAssetsApi.as_view(),
name='user-direct-assets'),
path('<str:user>/nodes/favorite/assets/', api.UserFavoriteAssetsApi.as_view(),
name='user-favorite-assets'),
path('<str:user>/nodes/<uuid:node_id>/assets/', api.UserPermedNodeAssetsApi.as_view(),
name='user-node-assets'),
# nodes
path('<str:user>/nodes/', api.UserAllPermedNodesApi.as_view(),
name='user-all-nodes'),
path('<str:user>/nodes/children/', api.UserPermedNodeChildrenApi.as_view(),
name='user-node-children'),
# 授权树只通过这一个 API 获取, 类似资产树
path('<str:user>/nodes/children/tree/', api.UserPermNodeChildrenAsTreeApi.as_view(),
name='user-perm-node-children-tree'),
# tree-asset
# path('<str:user>/assets/tree/', api.UserAllPermedAssetsAsTreeApi.as_view(),
# name='user-direct-assets-as-tree'),
# path('<str:user>/ungroup/assets/tree/', api.UserUngroupAssetsAsTreeApi.as_view(),
# name='user-ungroup-assets-as-tree'),
# tree-node不包含资产
# path('<str:user>/nodes/tree/', api.UserAllPermedNodesAsTreeApi.as_view(),
# name='user-all-nodes-as-tree'),
# path('<str:user>/nodes/children/tree/', api.UserPermedNodeChildrenAsTreeApi.as_view(),
# name='user-node-children-as-tree'),
# tree-node-with-asset
# 异步树
# path('<str:user>/nodes/children-with-assets/tree/',
# api.UserPermedNodeChildrenWithAssetsAsTreeApi.as_view(),
# name='user-node-children-with-assets-as-tree'),
# path('<str:user>/nodes/children-with-assets/category/tree/',
# api.UserPermedNodeChildrenWithAssetsAsCategoryTreeApi.as_view(),
# name='user-node-children-with-assets-as-category-tree'),
# 同步树
# path('<str:user>/nodes/all-with-assets/tree/',
# api.UserPermedNodesWithAssetsAsTreeApi.as_view(),
# name='user-nodes-with-assets-as-tree'),
path('<str:user>/assets/<uuid:pk>/', api.UserPermedAssetRetrieveApi.as_view(), name='user-permed-asset'),
path('<str:user>/assets/', api.UserAllPermedAssetsApi.as_view(), name='user-all-assets'),
path('<str:user>/nodes/favorite/assets/', api.UserFavoriteAssetsApi.as_view(), name='user-favorite-assets'),
# 用户授权树只通过这一个 API 获取, 类似资产树
path('<str:user>/nodes/children/tree/', api.UserPermNodeChildrenAsTreeApi.as_view(), name='user-perm-node-children-tree'),
]
user_group_permission_urlpatterns = [

View File

@@ -2,9 +2,9 @@
from django.db.models import Q
from common.utils import timeit, lazyproperty, get_logger, is_uuid
from orgs.utils import current_org
from orgs.utils import current_org, tmp_to_org
from users.models import User
from assets.models import Node, Asset
from assets.models import Node, Asset, FavoriteAsset
from perms.models import AssetPermission
@@ -133,7 +133,37 @@ class UserPermUtil(object):
q = Q(node_id__in=dn_all_nodes_ids)
q |= Q(node_id__in=other_nodes_ids) & Q(id__in=self._user_direct_asset_ids)
assets = Asset.objects.filter(org_id=self._org.id).filter(q)
assets = Asset.objects.filter(org_id=self._org.id).filter(q).valid()
return assets
def get_all_assets_of_org(self):
with tmp_to_org(self._org):
root_node = Node.org_root()
assets = self.get_node_all_assets(root_node)
return assets
@classmethod
def get_all_assets(cls, user):
if current_org.is_root():
orgs = user.orgs.all()
else:
orgs = user.orgs.filter(id=current_org.id)
assets = Asset.objects.none()
for org in orgs:
_util = cls(user=user, org=org)
org_assets = _util.get_all_assets_of_org()
assets |= org_assets
return assets
@classmethod
def get_favorite_assets(cls, user: User):
asset_ids = FavoriteAsset.get_user_favorite_asset_ids(user)
assets = Asset.objects.filter(id__in=asset_ids).valid()
return assets
def get_ungrouped_assets(self):
assets = Asset.objects.filter(id__in=self._user_direct_asset_ids).valid()
return assets
def _uuids_to_string(self, uuids):