From 4718b2615803618c940850f3e5363b429d6a5dba Mon Sep 17 00:00:00 2001 From: nero <827392902@qq.com> Date: Thu, 17 Aug 2023 20:42:53 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(tenant=5Forganization):=20=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=8D=95=E4=B8=AA=E9=83=A8=E9=97=A8=E7=9A=84=E4=BA=8C?= =?UTF-8?q?=E7=BA=A7=E9=83=A8=E9=97=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 获取单个部门的二级部门 feat #1129 --- .../apis/web/tenant_organization/__init__.py | 10 ++++ .../web/tenant_organization/serializers.py | 54 +++++++++++++++++++ .../apis/web/tenant_organization/urls.py | 17 ++++++ .../apis/web/tenant_organization/views.py | 32 +++++++++++ src/bk-user/bkuser/apis/web/urls.py | 1 + src/bk-user/bkuser/biz/data_source.py | 2 +- src/bk-user/bkuser/biz/tenant.py | 44 ++++++++++++++- 7 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 src/bk-user/bkuser/apis/web/tenant_organization/__init__.py create mode 100644 src/bk-user/bkuser/apis/web/tenant_organization/serializers.py create mode 100644 src/bk-user/bkuser/apis/web/tenant_organization/urls.py create mode 100644 src/bk-user/bkuser/apis/web/tenant_organization/views.py diff --git a/src/bk-user/bkuser/apis/web/tenant_organization/__init__.py b/src/bk-user/bkuser/apis/web/tenant_organization/__init__.py new file mode 100644 index 000000000..1060b7bf4 --- /dev/null +++ b/src/bk-user/bkuser/apis/web/tenant_organization/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" diff --git a/src/bk-user/bkuser/apis/web/tenant_organization/serializers.py b/src/bk-user/bkuser/apis/web/tenant_organization/serializers.py new file mode 100644 index 000000000..59eccd145 --- /dev/null +++ b/src/bk-user/bkuser/apis/web/tenant_organization/serializers.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import logging + +from drf_yasg.utils import swagger_serializer_method +from rest_framework import serializers + +from bkuser.apps.data_source.models import DataSourceDepartmentRelation +from bkuser.biz.tenant import TenantDepartmentHandler + +logger = logging.getLogger(__name__) + + +class TenantDepartmentOutputSchema(serializers.Serializer): + id = serializers.IntegerField(help_text="租户部门ID") + name = serializers.CharField(help_text="部门名称") + has_children = serializers.BooleanField(help_text="子部门信息") + + +class TenantDepartmentChildrenListOutputSLZ(serializers.Serializer): + departments = serializers.SerializerMethodField() + + def _list_departments_info(self, current_tenant_id, departments): + data = [] + # 此处已经过滤出和当前租户绑定组织 + tenant_department_map = TenantDepartmentHandler.get_tenant_departments_info_map( + current_tenant_id, [item.department.id for item in departments] + ) + for item in departments: + # NOTE:协同数据源,部分根部门并未授权到当前部门 + tenant_department = tenant_department_map.get(item.department.id, None) + if not tenant_department: + logger.info(f"DataSourceDepartment<{item.department.id}> does not bind Tenant<{current_tenant_id}>") + continue + data.append(tenant_department.model_dump()) + return data + + def _get_children_department(self, instance): + data_source_department_id = instance.data_source_department_id + department = DataSourceDepartmentRelation.objects.get(department_id=data_source_department_id) + children = department.get_children() + return self._list_departments_info(instance.tenant_id, children) + + @swagger_serializer_method(serializer_or_field=TenantDepartmentOutputSchema(many=True)) + def get_departments(self, instance): + return self._get_children_department(instance) diff --git a/src/bk-user/bkuser/apis/web/tenant_organization/urls.py b/src/bk-user/bkuser/apis/web/tenant_organization/urls.py new file mode 100644 index 000000000..dddecff1b --- /dev/null +++ b/src/bk-user/bkuser/apis/web/tenant_organization/urls.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from django.urls import path + +from . import views + +urlpatterns = [ + path("departments//children/", views.TenantDepartmentChildrenListApi.as_view()), +] diff --git a/src/bk-user/bkuser/apis/web/tenant_organization/views.py b/src/bk-user/bkuser/apis/web/tenant_organization/views.py new file mode 100644 index 000000000..5300869ef --- /dev/null +++ b/src/bk-user/bkuser/apis/web/tenant_organization/views.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import logging + +from drf_yasg.utils import swagger_auto_schema +from rest_framework import generics, status + +from bkuser.apis.web.tenant_organization.serializers import TenantDepartmentChildrenListOutputSLZ +from bkuser.apps.tenant.models import TenantDepartment + +logger = logging.getLogger(__name__) + + +class TenantDepartmentChildrenListApi(generics.RetrieveAPIView): + lookup_url_kwarg = "id" + queryset = TenantDepartment.objects.all() + serializer_class = TenantDepartmentChildrenListOutputSLZ + + @swagger_auto_schema( + operation_description="租户部门的二级子部门", + responses={status.HTTP_200_OK: TenantDepartmentChildrenListOutputSLZ()}, + ) + def get(self, request, *args, **kwargs): + return self.retrieve(request, *args, **kwargs) diff --git a/src/bk-user/bkuser/apis/web/urls.py b/src/bk-user/bkuser/apis/web/urls.py index 3a11320cf..103a4f5c5 100644 --- a/src/bk-user/bkuser/apis/web/urls.py +++ b/src/bk-user/bkuser/apis/web/urls.py @@ -15,4 +15,5 @@ path("basic/", include("bkuser.apis.web.basic.urls")), # 租户 path("tenants/", include("bkuser.apis.web.tenant.urls")), + path("tenant-organization-tree/", include("bkuser.apis.web.tenant_organization.urls")), ] diff --git a/src/bk-user/bkuser/biz/data_source.py b/src/bk-user/bkuser/biz/data_source.py index 5bdaa0f9e..338ed5010 100644 --- a/src/bk-user/bkuser/biz/data_source.py +++ b/src/bk-user/bkuser/biz/data_source.py @@ -27,7 +27,7 @@ def get_data_source_map_by_owner( owner_tenant_ids: Optional[List[str]] = None, ) -> Dict[str, List[DataSourceSimpleInfo]]: """ - 查询数据源 + 查询租户下数据源 """ data_sources = DataSource.objects.all() if owner_tenant_ids is not None: diff --git a/src/bk-user/bkuser/biz/tenant.py b/src/bk-user/bkuser/biz/tenant.py index 7f4d40f41..d9522cfcc 100644 --- a/src/bk-user/bkuser/biz/tenant.py +++ b/src/bk-user/bkuser/biz/tenant.py @@ -14,8 +14,8 @@ from django.db import transaction from pydantic import BaseModel -from bkuser.apps.data_source.models import DataSource, DataSourcePlugin, DataSourceUser -from bkuser.apps.tenant.models import Tenant, TenantManager, TenantUser +from bkuser.apps.data_source.models import DataSource, DataSourceDepartmentRelation, DataSourcePlugin, DataSourceUser +from bkuser.apps.tenant.models import Tenant, TenantDepartment, TenantManager, TenantUser from bkuser.utils.uuid import generate_uuid @@ -67,6 +67,16 @@ class TenantManagerWithoutID(BaseModel): phone_country_code: str +class TenantDepartmentBaseInfo(BaseModel): + """ + 部门基础信息 + """ + + tenant_department_id: int + name: str + has_children: bool + + class TenantUserHandler: @staticmethod def list_tenant_user_by_id(tenant_user_ids: List[str]) -> List[TenantUserWithInheritedInfo]: @@ -186,3 +196,33 @@ def update_with_managers(tenant_id: str, tenant_info: TenantEditableBaseInfo, ma [TenantManager(tenant_id=tenant_id, tenant_user_id=i) for i in should_add_manager_ids], batch_size=100, ) + + +class TenantDepartmentHandler: + @staticmethod + def get_tenant_departments_info_map( + tenant_id: str, data_source_departments: Optional[List[int]] = None + ) -> Dict[int, TenantDepartmentBaseInfo]: + """ + 获取租户的部门映射 + """ + # tenant_id 租户下部门关系映射 + tenant_departments = TenantDepartment.objects.filter(tenant_id=tenant_id) + if data_source_departments: + tenant_departments = tenant_departments.filter(data_source_department_id__in=data_source_departments) + # 构建映射关系数据 + departments_map: dict = {} + for item in tenant_departments: + # NOTE: 协同数据源,可能存在未授权全部子部门 + children_ids = DataSourceDepartmentRelation.objects.filter( + parent_id=item.data_source_department.id + ).values_list("department_id", flat=True) + has_children = TenantDepartment.objects.filter(data_source_department_id__in=children_ids).exists() + departments_map[item.data_source_department.id] = TenantDepartmentBaseInfo( + **{ + "tenant_department_id": item.id, + "name": item.data_source_department.name, + "has_children": has_children, + } + ) + return departments_map From 7755b3ef1d85d2919cbc947b0c9fb2bc5ead11ca Mon Sep 17 00:00:00 2001 From: nero <827392902@qq.com> Date: Tue, 22 Aug 2023 11:29:55 +0800 Subject: [PATCH 2/2] =?UTF-8?q?code=20review=20=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__init__.py | 0 .../apis/web/organization/serializers.py | 21 ++++ .../urls.py | 6 +- .../views.py | 20 ++-- .../web/tenant_organization/serializers.py | 54 --------- src/bk-user/bkuser/apis/web/urls.py | 2 +- src/bk-user/bkuser/biz/data_source.py | 42 ++++++- src/bk-user/bkuser/biz/tenant.py | 110 +++++++++++++----- 8 files changed, 163 insertions(+), 92 deletions(-) rename src/bk-user/bkuser/apis/web/{tenant_organization => organization}/__init__.py (100%) create mode 100644 src/bk-user/bkuser/apis/web/organization/serializers.py rename src/bk-user/bkuser/apis/web/{tenant_organization => organization}/urls.py (83%) rename src/bk-user/bkuser/apis/web/{tenant_organization => organization}/views.py (58%) delete mode 100644 src/bk-user/bkuser/apis/web/tenant_organization/serializers.py diff --git a/src/bk-user/bkuser/apis/web/tenant_organization/__init__.py b/src/bk-user/bkuser/apis/web/organization/__init__.py similarity index 100% rename from src/bk-user/bkuser/apis/web/tenant_organization/__init__.py rename to src/bk-user/bkuser/apis/web/organization/__init__.py diff --git a/src/bk-user/bkuser/apis/web/organization/serializers.py b/src/bk-user/bkuser/apis/web/organization/serializers.py new file mode 100644 index 000000000..f796fba7a --- /dev/null +++ b/src/bk-user/bkuser/apis/web/organization/serializers.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import logging + +from rest_framework import serializers + +logger = logging.getLogger(__name__) + + +class TenantDepartmentChildrenListOutputSLZ(serializers.Serializer): + id = serializers.IntegerField(help_text="租户部门ID") + name = serializers.CharField(help_text="部门名称") + has_children = serializers.BooleanField(help_text="是否有子部门") diff --git a/src/bk-user/bkuser/apis/web/tenant_organization/urls.py b/src/bk-user/bkuser/apis/web/organization/urls.py similarity index 83% rename from src/bk-user/bkuser/apis/web/tenant_organization/urls.py rename to src/bk-user/bkuser/apis/web/organization/urls.py index dddecff1b..bba98c32c 100644 --- a/src/bk-user/bkuser/apis/web/tenant_organization/urls.py +++ b/src/bk-user/bkuser/apis/web/organization/urls.py @@ -13,5 +13,9 @@ from . import views urlpatterns = [ - path("departments//children/", views.TenantDepartmentChildrenListApi.as_view()), + path( + "departments//children/", + views.TenantDepartmentChildrenListApi.as_view(), + name="organization.children.list", + ), ] diff --git a/src/bk-user/bkuser/apis/web/tenant_organization/views.py b/src/bk-user/bkuser/apis/web/organization/views.py similarity index 58% rename from src/bk-user/bkuser/apis/web/tenant_organization/views.py rename to src/bk-user/bkuser/apis/web/organization/views.py index 5300869ef..bacc99c6d 100644 --- a/src/bk-user/bkuser/apis/web/tenant_organization/views.py +++ b/src/bk-user/bkuser/apis/web/organization/views.py @@ -12,21 +12,25 @@ from drf_yasg.utils import swagger_auto_schema from rest_framework import generics, status +from rest_framework.response import Response -from bkuser.apis.web.tenant_organization.serializers import TenantDepartmentChildrenListOutputSLZ -from bkuser.apps.tenant.models import TenantDepartment +from bkuser.apis.web.organization.serializers import TenantDepartmentChildrenListOutputSLZ +from bkuser.biz.tenant import TenantDepartmentHandler logger = logging.getLogger(__name__) -class TenantDepartmentChildrenListApi(generics.RetrieveAPIView): - lookup_url_kwarg = "id" - queryset = TenantDepartment.objects.all() +class TenantDepartmentChildrenListApi(generics.ListAPIView): + pagination_class = None serializer_class = TenantDepartmentChildrenListOutputSLZ @swagger_auto_schema( - operation_description="租户部门的二级子部门", - responses={status.HTTP_200_OK: TenantDepartmentChildrenListOutputSLZ()}, + operation_description="租户部门的二级子部门列表", + responses={status.HTTP_200_OK: TenantDepartmentChildrenListOutputSLZ(many=True)}, ) def get(self, request, *args, **kwargs): - return self.retrieve(request, *args, **kwargs) + tenant_department_id = self.kwargs["id"] + # 拉取子部门信息列表 + tenant_department_children = TenantDepartmentHandler.get_tenant_department_children_by_id(tenant_department_id) + data = [item.model_dump(include={"id", "name", "has_children"}) for item in tenant_department_children] + return Response(self.get_serializer(data, many=True).data) diff --git a/src/bk-user/bkuser/apis/web/tenant_organization/serializers.py b/src/bk-user/bkuser/apis/web/tenant_organization/serializers.py deleted file mode 100644 index 59eccd145..000000000 --- a/src/bk-user/bkuser/apis/web/tenant_organization/serializers.py +++ /dev/null @@ -1,54 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available. -Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. -Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. -You may obtain a copy of the License at http://opensource.org/licenses/MIT -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. -""" -import logging - -from drf_yasg.utils import swagger_serializer_method -from rest_framework import serializers - -from bkuser.apps.data_source.models import DataSourceDepartmentRelation -from bkuser.biz.tenant import TenantDepartmentHandler - -logger = logging.getLogger(__name__) - - -class TenantDepartmentOutputSchema(serializers.Serializer): - id = serializers.IntegerField(help_text="租户部门ID") - name = serializers.CharField(help_text="部门名称") - has_children = serializers.BooleanField(help_text="子部门信息") - - -class TenantDepartmentChildrenListOutputSLZ(serializers.Serializer): - departments = serializers.SerializerMethodField() - - def _list_departments_info(self, current_tenant_id, departments): - data = [] - # 此处已经过滤出和当前租户绑定组织 - tenant_department_map = TenantDepartmentHandler.get_tenant_departments_info_map( - current_tenant_id, [item.department.id for item in departments] - ) - for item in departments: - # NOTE:协同数据源,部分根部门并未授权到当前部门 - tenant_department = tenant_department_map.get(item.department.id, None) - if not tenant_department: - logger.info(f"DataSourceDepartment<{item.department.id}> does not bind Tenant<{current_tenant_id}>") - continue - data.append(tenant_department.model_dump()) - return data - - def _get_children_department(self, instance): - data_source_department_id = instance.data_source_department_id - department = DataSourceDepartmentRelation.objects.get(department_id=data_source_department_id) - children = department.get_children() - return self._list_departments_info(instance.tenant_id, children) - - @swagger_serializer_method(serializer_or_field=TenantDepartmentOutputSchema(many=True)) - def get_departments(self, instance): - return self._get_children_department(instance) diff --git a/src/bk-user/bkuser/apis/web/urls.py b/src/bk-user/bkuser/apis/web/urls.py index 103a4f5c5..799bb7be2 100644 --- a/src/bk-user/bkuser/apis/web/urls.py +++ b/src/bk-user/bkuser/apis/web/urls.py @@ -15,5 +15,5 @@ path("basic/", include("bkuser.apis.web.basic.urls")), # 租户 path("tenants/", include("bkuser.apis.web.tenant.urls")), - path("tenant-organization-tree/", include("bkuser.apis.web.tenant_organization.urls")), + path("tenant-organization/", include("bkuser.apis.web.organization.urls")), ] diff --git a/src/bk-user/bkuser/biz/data_source.py b/src/bk-user/bkuser/biz/data_source.py index 338ed5010..f7ef53f27 100644 --- a/src/bk-user/bkuser/biz/data_source.py +++ b/src/bk-user/bkuser/biz/data_source.py @@ -13,7 +13,13 @@ from pydantic import BaseModel -from bkuser.apps.data_source.models import DataSource +from bkuser.apps.data_source.models import DataSource, DataSourceDepartment, DataSourceDepartmentRelation + + +class DataSourceDepartmentInfoWithChildren(BaseModel): + id: int + name: str + children: List[int] class DataSourceSimpleInfo(BaseModel): @@ -27,7 +33,7 @@ def get_data_source_map_by_owner( owner_tenant_ids: Optional[List[str]] = None, ) -> Dict[str, List[DataSourceSimpleInfo]]: """ - 查询租户下数据源 + 查询数据源 """ data_sources = DataSource.objects.all() if owner_tenant_ids is not None: @@ -38,3 +44,35 @@ def get_data_source_map_by_owner( data[i.owner_tenant_id].append(DataSourceSimpleInfo(id=i.id, name=i.name)) return data + + @staticmethod + def get_data_sources_by_tenant(tenant_ids: List[str]) -> Dict[str, List[int]]: + # 当前属于租户的数据源 + tenant_data_source_map: Dict = {} + data_sources = DataSource.objects.filter(owner_tenant_id__in=tenant_ids).values("id", "owner_tenant_id") + for item in data_sources: + tenant_id = item["owner_tenant_id"] + if tenant_id in tenant_data_source_map: + tenant_data_source_map[tenant_id].append(item["id"]) + else: + tenant_data_source_map[tenant_id] = [item["id"]] + # TODO 协同数据源获取 + return tenant_data_source_map + + +class DataSourceDepartmentHandler: + @staticmethod + def get_department_info_by_id(department_ids: List[int]) -> Dict[int, DataSourceDepartmentInfoWithChildren]: + """ + 获取部门基础信息 + """ + departments = DataSourceDepartment.objects.filter(id__in=department_ids) + departments_map: Dict = {} + for item in departments: + children = DataSourceDepartmentRelation.objects.get(department=item).get_children() + departments_map[item.id] = DataSourceDepartmentInfoWithChildren( + id=item.id, + name=item.name, + children=list(children.values_list("department_id", flat=True)), + ) + return departments_map diff --git a/src/bk-user/bkuser/biz/tenant.py b/src/bk-user/bkuser/biz/tenant.py index d9522cfcc..1b63782b8 100644 --- a/src/bk-user/bkuser/biz/tenant.py +++ b/src/bk-user/bkuser/biz/tenant.py @@ -16,6 +16,7 @@ from bkuser.apps.data_source.models import DataSource, DataSourceDepartmentRelation, DataSourcePlugin, DataSourceUser from bkuser.apps.tenant.models import Tenant, TenantDepartment, TenantManager, TenantUser +from bkuser.biz.data_source import DataSourceDepartmentHandler, DataSourceHandler from bkuser.utils.uuid import generate_uuid @@ -68,11 +69,7 @@ class TenantManagerWithoutID(BaseModel): class TenantDepartmentBaseInfo(BaseModel): - """ - 部门基础信息 - """ - - tenant_department_id: int + id: int name: str has_children: bool @@ -133,6 +130,16 @@ def get_tenant_manager_map(tenant_ids: Optional[List[str]] = None) -> Dict[str, return data + @staticmethod + def retrieve_tenant_managers(tenant_id: str) -> List[TenantUserWithInheritedInfo]: + """ + 查询单个租户的租户管理员 + """ + tenant_managers = TenantManager.objects.filter(tenant_id=tenant_id) + # 查询管理员对应的信息 + tenant_user_ids = [i.tenant_user_id for i in tenant_managers] + return TenantUserHandler.list_tenant_user_by_id(tenant_user_ids) + @staticmethod def create_with_managers(tenant_info: TenantBaseInfo, managers: List[TenantManagerWithoutID]) -> str: """ @@ -200,29 +207,80 @@ def update_with_managers(tenant_id: str, tenant_info: TenantEditableBaseInfo, ma class TenantDepartmentHandler: @staticmethod - def get_tenant_departments_info_map( - tenant_id: str, data_source_departments: Optional[List[int]] = None - ) -> Dict[int, TenantDepartmentBaseInfo]: + def convert_data_source_department_to_tenant_department( + tenant_id: str, data_source_department_ids: List[int] + ) -> List[TenantDepartmentBaseInfo]: """ - 获取租户的部门映射 + 转换为租户部门 """ # tenant_id 租户下部门关系映射 tenant_departments = TenantDepartment.objects.filter(tenant_id=tenant_id) - if data_source_departments: - tenant_departments = tenant_departments.filter(data_source_department_id__in=data_source_departments) - # 构建映射关系数据 - departments_map: dict = {} - for item in tenant_departments: - # NOTE: 协同数据源,可能存在未授权全部子部门 - children_ids = DataSourceDepartmentRelation.objects.filter( - parent_id=item.data_source_department.id - ).values_list("department_id", flat=True) - has_children = TenantDepartment.objects.filter(data_source_department_id__in=children_ids).exists() - departments_map[item.data_source_department.id] = TenantDepartmentBaseInfo( - **{ - "tenant_department_id": item.id, - "name": item.data_source_department.name, - "has_children": has_children, - } + + # 获取数据源部门基础信息 + data_source_departments = DataSourceDepartmentHandler.get_department_info_by_id(data_source_department_ids) + + # data_source_departments中包含了父子部门的ID,协同数据源需要查询绑定了该租户 + department_ids = list(data_source_departments.keys()) + for department in data_source_departments.values(): + department_ids += department.children + + # NOTE: 协同数据源,可能存在未授权全部子部门 + # 提前拉取所有映射, 过滤绑定的租户部门 + tenant_departments = tenant_departments.filter(data_source_department_id__in=department_ids) + if not tenant_departments.exists(): + return [] + + # 已绑定该租户的数据源部门id + bound_departments_ids = tenant_departments.values_list("data_source_department_id", flat=True) + + # 构建返回数据 + data: List[TenantDepartmentBaseInfo] = [] + for tenant_department in tenant_departments: + # tenant_departments 包含了父子部门的租户映射关系,但是子部门非本次查询的入参,跳过 + data_source_department_id = tenant_department.data_source_department_id + if data_source_department_id not in data_source_department_ids: + continue + # 部门基础信息 + data_source_department_info = data_source_departments[data_source_department_id] + # 只要一个子部门被授权,都是存在子部门 + children_flag = [True for child in data_source_department_info.children if child in bound_departments_ids] + data.append( + TenantDepartmentBaseInfo( + id=tenant_department.id, + name=data_source_department_info.name, + has_children=any(children_flag), + ) ) - return departments_map + return data + + @staticmethod + def get_tenant_root_departments_by_id( + tenant_ids: List[str], convert_tenant_id: str + ) -> Dict[str, List[TenantDepartmentBaseInfo]]: + data_source_map = DataSourceHandler.get_data_sources_by_tenant(tenant_ids) + + # 通过获取数据源的根节点 + tenant_root_department_map: Dict = {} + for tenant_id, data_source_ids in data_source_map.items(): + root_department_ids = ( + DataSourceDepartmentRelation.objects.root_nodes() + .filter(data_source_id__in=data_source_ids) + .values_list("department_id", flat=True) + ) + # 转换数据源部门为当前为 convert_tenant_id租户的租户部门 + tenant_root_department = TenantDepartmentHandler.convert_data_source_department_to_tenant_department( + tenant_id=convert_tenant_id, data_source_department_ids=list(root_department_ids) + ) + tenant_root_department_map[tenant_id] = tenant_root_department + return tenant_root_department_map + + @staticmethod + def get_tenant_department_children_by_id(tenant_department_id: int) -> List[TenantDepartmentBaseInfo]: + tenant_department = TenantDepartment.objects.get(id=tenant_department_id) + # 获取二级组织 + children = DataSourceDepartmentRelation.objects.get( + department=tenant_department.data_source_department + ).get_children() + return TenantDepartmentHandler.convert_data_source_department_to_tenant_department( + tenant_department.tenant_id, children.values_list("department_id", flat=True) + )