Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tenant_organization): 获取单个部门的二级部门 #1172

Merged
merged 2 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/bk-user/bkuser/apis/web/organization/__init__.py
Original file line number Diff line number Diff line change
@@ -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.
"""
21 changes: 21 additions & 0 deletions src/bk-user/bkuser/apis/web/organization/serializers.py
Original file line number Diff line number Diff line change
@@ -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="是否有子部门")
21 changes: 21 additions & 0 deletions src/bk-user/bkuser/apis/web/organization/urls.py
Original file line number Diff line number Diff line change
@@ -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.
"""
from django.urls import path

from . import views

urlpatterns = [
path(
"departments/<int:id>/children/",
views.TenantDepartmentChildrenListApi.as_view(),
name="organization.children.list",
),
]
36 changes: 36 additions & 0 deletions src/bk-user/bkuser/apis/web/organization/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# -*- 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 rest_framework.response import Response

from bkuser.apis.web.organization.serializers import TenantDepartmentChildrenListOutputSLZ
from bkuser.biz.tenant import TenantDepartmentHandler

logger = logging.getLogger(__name__)


class TenantDepartmentChildrenListApi(generics.ListAPIView):
pagination_class = None
serializer_class = TenantDepartmentChildrenListOutputSLZ

@swagger_auto_schema(
operation_description="租户部门的二级子部门列表",
responses={status.HTTP_200_OK: TenantDepartmentChildrenListOutputSLZ(many=True)},
)
def get(self, 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)
neronkl marked this conversation as resolved.
Show resolved Hide resolved
neronkl marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions src/bk-user/bkuser/apis/web/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@
path("basic/", include("bkuser.apis.web.basic.urls")),
# 租户
path("tenants/", include("bkuser.apis.web.tenant.urls")),
path("tenant-organization/", include("bkuser.apis.web.organization.urls")),
]
40 changes: 39 additions & 1 deletion src/bk-user/bkuser/biz/data_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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
neronkl marked this conversation as resolved.
Show resolved Hide resolved


class DataSourceDepartmentHandler:
@staticmethod
def get_department_info_by_id(department_ids: List[int]) -> Dict[int, DataSourceDepartmentInfoWithChildren]:
neronkl marked this conversation as resolved.
Show resolved Hide resolved
"""
获取部门基础信息
"""
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)),
)
neronkl marked this conversation as resolved.
Show resolved Hide resolved
return departments_map
102 changes: 100 additions & 2 deletions src/bk-user/bkuser/biz/tenant.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
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.biz.data_source import DataSourceDepartmentHandler, DataSourceHandler
from bkuser.utils.uuid import generate_uuid


Expand Down Expand Up @@ -67,6 +68,12 @@ class TenantManagerWithoutID(BaseModel):
phone_country_code: str


class TenantDepartmentBaseInfo(BaseModel):
id: int
name: str
has_children: bool


class TenantUserHandler:
@staticmethod
def list_tenant_user_by_id(tenant_user_ids: List[str]) -> List[TenantUserWithInheritedInfo]:
Expand Down Expand Up @@ -123,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)
neronkl marked this conversation as resolved.
Show resolved Hide resolved

@staticmethod
def create_with_managers(tenant_info: TenantBaseInfo, managers: List[TenantManagerWithoutID]) -> str:
"""
Expand Down Expand Up @@ -186,3 +203,84 @@ 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 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)

# 获取数据源部门基础信息
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),
)
)
neronkl marked this conversation as resolved.
Show resolved Hide resolved
return data

@staticmethod
def get_tenant_root_departments_by_id(
neronkl marked this conversation as resolved.
Show resolved Hide resolved
tenant_ids: List[str], convert_tenant_id: str
) -> Dict[str, List[TenantDepartmentBaseInfo]]:
neronkl marked this conversation as resolved.
Show resolved Hide resolved
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)
)
Loading