From 4f8e0fc28e077f8cd57053824b3a1f7d07dc3d43 Mon Sep 17 00:00:00 2001
From: Bai <baijiangjie@gmail.com>
Date: Fri, 23 Dec 2022 13:19:38 +0800
Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E6=8E=88=E6=9D=83?=
 =?UTF-8?q?=E6=A0=91API=EF=BC=8C=E8=BF=9E=E7=BB=AD=E5=88=B7=E6=96=B03?=
 =?UTF-8?q?=E6=AC=A1=E8=BD=AC=E4=B8=BA=E5=BC=BA=E5=88=B6=E5=88=B7=E6=96=B0?=
 =?UTF-8?q?=EF=BC=9B=E4=BF=AE=E6=94=B9=E5=BC=82=E6=AD=A5=E6=8E=88=E6=9D=83?=
 =?UTF-8?q?=E6=A0=91=E4=B8=80=E7=BA=A7=E8=8A=82=E7=82=B9=E7=9A=84=E8=B5=84?=
 =?UTF-8?q?=E4=BA=A7=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98=EF=BC=9B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 apps/perms/api/user_permission/tree/mixin.py  | 30 ++++++++++++++--
 .../user_permission/tree/node_with_asset.py   | 35 ++++++++++---------
 apps/perms/utils/user_perm.py                 | 23 +++++++-----
 3 files changed, 60 insertions(+), 28 deletions(-)

diff --git a/apps/perms/api/user_permission/tree/mixin.py b/apps/perms/api/user_permission/tree/mixin.py
index 6c5eaac47..c0cafe37c 100644
--- a/apps/perms/api/user_permission/tree/mixin.py
+++ b/apps/perms/api/user_permission/tree/mixin.py
@@ -1,6 +1,9 @@
+from django.core.cache import cache
+
 from rest_framework.request import Request
 
 from common.http import is_true
+from common.utils import lazyproperty
 from perms.utils import UserPermTreeRefreshUtil
 from users.models import User
 
@@ -9,8 +12,29 @@ __all__ = ['RebuildTreeMixin']
 
 class RebuildTreeMixin:
     user: User
+    request: Request
 
-    def get(self, request: Request, *args, **kwargs):
-        force = is_true(request.query_params.get('rebuild_tree'))
-        UserPermTreeRefreshUtil(self.user).refresh_if_need(force)
+    def get(self, request, *args, **kwargs):
+        UserPermTreeRefreshUtil(self.user).refresh_if_need(force=self.is_force_refresh_tree)
         return super().get(request, *args, **kwargs)
+
+    @lazyproperty
+    def is_force_refresh_tree(self):
+        force = is_true(self.request.query_params.get('rebuild_tree'))
+        if not force:
+            force = self.compute_is_force_refresh()
+        return force
+
+    def compute_is_force_refresh(self):
+        """ 5s 内连续刷新三次转为强制刷新 """
+        force_timeout = 5
+        force_max_count = 3
+        force_cache_key = '{user_id}:{path}'.format(user_id=self.user.id, path=self.request.path)
+        count = cache.get(force_cache_key, 1)
+        if count >= force_max_count:
+            force = True
+            cache.delete(force_cache_key)
+        else:
+            force = False
+            cache.set(force_cache_key, count+1, force_timeout)
+        return force
diff --git a/apps/perms/api/user_permission/tree/node_with_asset.py b/apps/perms/api/user_permission/tree/node_with_asset.py
index bb36b77ef..1dcdcc43d 100644
--- a/apps/perms/api/user_permission/tree/node_with_asset.py
+++ b/apps/perms/api/user_permission/tree/node_with_asset.py
@@ -37,7 +37,7 @@ class BaseUserNodeWithAssetAsTreeApi(
     def list(self, request, *args, **kwargs):
         nodes, assets = self.get_nodes_assets()
         tree_nodes = self.serialize_nodes(nodes, with_asset_amount=True)
-        tree_assets = self.serialize_assets(assets, node_key=self.node_key_for_serializer_assets)
+        tree_assets = self.serialize_assets(assets, node_key=self.node_key_for_serialize_assets)
         data = list(tree_nodes) + list(tree_assets)
         return Response(data=data)
 
@@ -46,7 +46,7 @@ class BaseUserNodeWithAssetAsTreeApi(
         return [], []
 
     @lazyproperty
-    def node_key_for_serializer_assets(self):
+    def node_key_for_serialize_assets(self):
         return None
 
 
@@ -95,19 +95,21 @@ class UserPermedNodesWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
 class UserPermedNodeChildrenWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
     """ 用户授权的节点的子节点与资产树 """
 
+    # 默认展开的节点key
+    default_unfolded_node_key = None
+
     def get_nodes_assets(self):
         query_node_util = UserPermNodeUtil(self.user)
         query_asset_util = UserPermAssetUtil(self.user)
         node_key = self.query_node_key
         if not node_key:
-            nodes = query_node_util.get_top_level_nodes()
-            assets = Asset.objects.none()
-            # 获取根节点下的资产
-            for node in nodes:
-                if not node.key.isdigit():
-                    continue
-                assets = query_asset_util.get_node_assets(key=node.key)
-                break
+            nodes, unfolded_node = query_node_util.get_top_level_nodes(with_unfolded_node=True)
+            if unfolded_node:
+                """ 默认展开的节点, 获取根节点下的资产 """
+                assets = query_asset_util.get_node_assets(key=unfolded_node.key)
+                self.default_unfolded_node_key = unfolded_node.key
+            else:
+                assets = Asset.objects.none()
         elif node_key == PermNode.UNGROUPED_NODE_KEY:
             nodes = PermNode.objects.none()
             assets = query_asset_util.get_ungroup_assets()
@@ -123,16 +125,15 @@ class UserPermedNodeChildrenWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
     @lazyproperty
     def query_node_key(self):
         node_key = self.request.query_params.get('key', None)
-        if node_key is not None:
-            return node_key
-        node_id = self.request.query_params.get('id', None)
-        node = get_object_or_none(Node, id=node_id)
-        node_key = getattr(node, 'key', None)
+        if node_key is None:
+            node_id = self.request.query_params.get('id', None)
+            node = get_object_or_none(Node, id=node_id)
+            node_key = getattr(node, 'key', None)
         return node_key
 
     @lazyproperty
-    def node_key_for_serializer_assets(self):
-        return self.query_node_key
+    def node_key_for_serialize_assets(self):
+        return self.query_node_key or self.default_unfolded_node_key
 
 
 class UserGrantedK8sAsTreeApi(SelfOrPKUserMixin, ListAPIView):
diff --git a/apps/perms/utils/user_perm.py b/apps/perms/utils/user_perm.py
index 96644c03e..ceea59b22 100644
--- a/apps/perms/utils/user_perm.py
+++ b/apps/perms/utils/user_perm.py
@@ -148,15 +148,20 @@ class UserPermNodeUtil:
         assets_amount = UserPermAssetUtil(self.user).get_direct_assets().count()
         return PermNode.get_favorite_node(assets_amount)
 
-    def get_top_level_nodes(self):
+    def get_top_level_nodes(self, with_unfolded_node=False):
+        # 是否有节点展开, 展开的节点
+        unfolded_node = None
         nodes = self.get_special_nodes()
-        # 获取组织下的根节点
-        real_nodes = self._get_indirect_perm_node_children(key='')
+        real_nodes = self._get_perm_node_children_from_relation(key='')
         nodes.extend(real_nodes)
         if len(real_nodes) == 1:
-            children = self.get_node_children(real_nodes[0].key)
+            unfolded_node = real_nodes[0]
+            children = self.get_node_children(unfolded_node.key)
             nodes.extend(children)
-        return nodes
+        if with_unfolded_node:
+            return nodes, unfolded_node
+        else:
+            return nodes
 
     def get_special_nodes(self):
         nodes = []
@@ -177,16 +182,18 @@ class UserPermNodeUtil:
         node = PermNode.objects.get(key=key)
         node.compute_node_from_and_assets_amount(self.user)
         if node.node_from == node.NodeFrom.granted:
+            """ 直接授权的节点, 直接从完整资产树获取子节点 """
             children = PermNode.objects.filter(parent_key=key)
         elif node.node_from in (node.NodeFrom.asset, node.NodeFrom.child):
-            children = self._get_indirect_perm_node_children(key)
+            """ 间接授权的节点, 从 Relation 表中获取子节点 """
+            children = self._get_perm_node_children_from_relation(key)
         else:
             children = PermNode.objects.none()
         children = sorted(children, key=lambda x: x.value)
         return children
 
-    def _get_indirect_perm_node_children(self, key):
-        """ 获取未直接授权节点的子节点 """
+    def _get_perm_node_children_from_relation(self, key):
+        """ 获取授权节点的子节点, 从用户授权节点关系表中获取 """
         children = PermNode.objects.filter(granted_node_rels__user=self.user, parent_key=key)
         children = children.annotate(**PermNode.annotate_granted_node_rel_fields).distinct()
         for node in children: