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: added query list of departments #2036

Merged
merged 14 commits into from
Jan 21, 2025
11 changes: 11 additions & 0 deletions src/bk-user/bkuser/apis/open_v3/serializers/department.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

from rest_framework import serializers

from bkuser.apps.tenant.models import TenantDepartment


class AncestorSLZ(serializers.Serializer):
id = serializers.IntegerField(help_text="祖先部门 ID")
Expand All @@ -34,3 +36,12 @@ class TenantDepartmentRetrieveOutputSLZ(serializers.Serializer):
id = serializers.IntegerField(help_text="部门 ID")
name = serializers.CharField(help_text="部门名称")
ancestors = serializers.ListField(help_text="祖先部门列表", required=False, child=AncestorSLZ(), allow_empty=True)


class TenantDepartmentListOutputSLZ(serializers.Serializer):
id = serializers.IntegerField(help_text="部门 ID")
name = serializers.CharField(help_text="部门名称", source="data_source_department.name")
parent_id = serializers.SerializerMethodField(help_text="父部门 ID", allow_null=True)

def get_parent_id(self, obj: TenantDepartment) -> int | None:
return self.context["parent_id_map"].get(obj.id)
rolin999 marked this conversation as resolved.
Show resolved Hide resolved
rolin999 marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 5 additions & 0 deletions src/bk-user/bkuser/apis/open_v3/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@
name="open_v3.tenant_department.retrieve",
),
path("users/", views.TenantUserListApi.as_view(), name="open_v3.tenant_user.list"),
path(
"departments/",
views.TenantDepartmentListApi.as_view(),
name="open_v3.tenant_department.list",
),
]
),
),
Expand Down
3 changes: 2 additions & 1 deletion src/bk-user/bkuser/apis/open_v3/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#
# We undertake not to change the open source license (MIT license) applicable
# to the current version of the project delivered to anyone in the future.
from .department import TenantDepartmentRetrieveApi
from .department import TenantDepartmentListApi, TenantDepartmentRetrieveApi
from .tenant import TenantListApi
from .user import (
TenantUserDepartmentListApi,
Expand All @@ -32,4 +32,5 @@
"TenantUserLeaderListApi",
"TenantUserListApi",
"TenantDepartmentRetrieveApi",
"TenantDepartmentListApi",
]
28 changes: 27 additions & 1 deletion src/bk-user/bkuser/apis/open_v3/views/department.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@

from bkuser.apis.open_v3.mixins import OpenApiCommonMixin
from bkuser.apis.open_v3.serializers.department import (
TenantDepartmentListOutputSLZ,
TenantDepartmentRetrieveInputSLZ,
TenantDepartmentRetrieveOutputSLZ,
)
from bkuser.apps.tenant.models import TenantDepartment
from bkuser.biz.organization import DataSourceDepartmentHandler
from bkuser.biz.organization import DataSourceDepartmentHandler, TenantDepartmentHandler


class TenantDepartmentRetrieveApi(OpenApiCommonMixin, generics.RetrieveAPIView):
Expand Down Expand Up @@ -66,3 +67,28 @@ def get(self, request, *args, **kwargs):
info["ancestors"] = [{"id": d.id, "name": d.data_source_department.name} for d in tenant_depts]

return Response(TenantDepartmentRetrieveOutputSLZ(info).data)


class TenantDepartmentListApi(OpenApiCommonMixin, generics.ListAPIView):
"""
获取部门列表
"""

@swagger_auto_schema(
tags=["open_v3.department"],
operation_id="list_department",
operation_description="查询部门列表",
responses={status.HTTP_200_OK: TenantDepartmentListOutputSLZ(many=True)},
)
def get(self, request, *args, **kwargs):
depts = TenantDepartment.objects.select_related("data_source_department").filter(tenant=self.tenant_id)

# 分页
page = self.paginate_queryset(depts)

# 查询 parent
parent_id_map = TenantDepartmentHandler.get_tenant_department_parent_id_map(self.tenant_id, page)

return self.get_paginated_response(
TenantDepartmentListOutputSLZ(page, many=True, context={"parent_id_map": parent_id_map}).data
)
36 changes: 35 additions & 1 deletion src/bk-user/bkuser/biz/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# to the current version of the project delivered to anyone in the future.

import datetime
from typing import List
from typing import Dict, List

from django.db import transaction
from django.utils import timezone
Expand All @@ -27,6 +27,7 @@
DataSourceUserDeprecatedPasswordRecord,
LocalDataSourceIdentityInfo,
)
from bkuser.apps.tenant.models import TenantDepartment
from bkuser.common.constants import PERMANENT_TIME
from bkuser.common.hashers import make_password

Expand Down Expand Up @@ -116,3 +117,36 @@ def get_dept_ancestors(dept_id: int) -> List[int]:
return []
# 返回的祖先部门默认以降序排列,从根祖先部门 -> 父部门
return list(relation.get_ancestors().values_list("department_id", flat=True))


class TenantDepartmentHandler:
@staticmethod
def get_tenant_department_parent_id_map(
tenant_id: str, tenant_departments: List[TenantDepartment]
) -> Dict[int, int]:
"""
获取部门的父部门 ID 映射
"""

# 获取部门的数据源部门 ID 列表
dept_ids = [dept.data_source_department_id for dept in tenant_departments]

# 获取部门的数据源部门关系
parent_id_map = dict(
DataSourceDepartmentRelation.objects.filter(department_id__in=dept_ids).values_list(
"department_id", "parent_id"
)
)
# 获取父部门数据源 ID 到租户父部门 ID 的映射
parent_ids = list(parent_id_map.values())
tenant_dept_id_map = dict(
TenantDepartment.objects.filter(tenant_id=tenant_id, data_source_department_id__in=parent_ids).values_list(
"data_source_department_id", "id"
)
)
rolin999 marked this conversation as resolved.
Show resolved Hide resolved

return {
dept.id: tenant_dept_id_map[parent_id_map[dept.data_source_department_id]]
for dept in tenant_departments
if parent_id_map[dept.data_source_department_id] in tenant_dept_id_map
}
47 changes: 47 additions & 0 deletions src/bk-user/support-files/apidocs/en/list_department.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
### Description

(Pagination) Query list of departments

### Parameters

| Name | Type | Required | Description |
|-----------|------|----------|---------------------------------------------|
| page | int | No | Page number, default is 1 |
| page_size | int | No | The number of pages per page, default is 10 |

### Request Example

```
// URL Query Parameters
page=2&page_size=2
```

### Response Example for Status Code 200

```json5
{
"data": {
"count": 2,
"results": [
{
"id": 3,
"name": "部门B",
"parent_id": 1,
},
{
"id": 4,
"name": "中心AA",
"parent_id": 2,
}
],
}
}
```

### Response Parameters Description

| Name | Type | Description |
|-----------|--------|-------------------------------------|
| id | int | Unique identifier of the department |
| name | string | The name of the department |
| parent_id | int | The parent department ID |
47 changes: 47 additions & 0 deletions src/bk-user/support-files/apidocs/zh/list_department.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
### 描述

(分页)查询部门列表

### 输入参数

| 参数名称 | 参数类型 | 必选 | 描述 |
|-----------|------|----|-------------|
| page | int | 否 | 页码,从 1 开始 |
| page_size | int | 否 | 每页数量,默认为 10 |

### 请求示例

```
// URL Query 参数
page=2&page_size=2
```

### 状态码 200 的响应示例

```json5
{
"data": {
"count": 2,
"results": [
{
"id": 3,
"name": "部门B",
"parent_id": 1,
},
{
"id": 4,
"name": "中心AA",
"parent_id": 2,
}
],
}
}
```

### 响应参数说明

| 参数名称 | 参数类型 | 描述 |
|-----------|--------|--------|
| id | int | 部门唯一标识 |
| name | string | 部门名称 |
| parent_id | int | 父部门 ID |
25 changes: 25 additions & 0 deletions src/bk-user/support-files/resources.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,28 @@ paths:
appVerifiedRequired: true
resourcePermissionRequired: true
descriptionEn: (Pagination) Query user's list

/api/v3/open/tenant/departments/:
nannan00 marked this conversation as resolved.
Show resolved Hide resolved
get:
operationId: list_department
description: 查询部门列表
tags: []
responses:
default:
description: ''
x-bk-apigateway-resource:
isPublic: true
allowApplyPermission: false
matchSubpath: false
backend:
name: default
method: get
path: /api/v3/open/tenant/departments/
matchSubpath: false
timeout: 0
pluginConfigs: []
authConfig:
userVerifiedRequired: false
appVerifiedRequired: true
resourcePermissionRequired: true
descriptionEn: Query list of the departments
53 changes: 53 additions & 0 deletions src/bk-user/tests/apis/open_v3/test_department.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,56 @@ def test_with_ancestors(self, api_client):
def test_with_not_found(self, api_client):
resp = api_client.get(reverse("open_v3.tenant_department.retrieve", kwargs={"id": 9999}))
assert resp.status_code == status.HTTP_404_NOT_FOUND


@pytest.mark.usefixtures("_init_tenant_users_depts")
class TestTenantDepartmentListApi:
def test_standard(self, api_client):
company = TenantDepartment.objects.get(data_source_department__name="公司")
dept_a = TenantDepartment.objects.get(data_source_department__name="部门A")
dept_b = TenantDepartment.objects.get(data_source_department__name="部门B")
center_aa = TenantDepartment.objects.get(data_source_department__name="中心AA")
center_ab = TenantDepartment.objects.get(data_source_department__name="中心AB")
center_ba = TenantDepartment.objects.get(data_source_department__name="中心BA")
group_aaa = TenantDepartment.objects.get(data_source_department__name="小组AAA")
group_aba = TenantDepartment.objects.get(data_source_department__name="小组ABA")
group_baa = TenantDepartment.objects.get(data_source_department__name="小组BAA")

resp = api_client.get(reverse("open_v3.tenant_department.list"))

assert resp.status_code == status.HTTP_200_OK
assert resp.data["count"] == 9
assert len(resp.data["results"]) == 9
assert [x["id"] for x in resp.data["results"]] == [
company.id,
dept_a.id,
dept_b.id,
center_aa.id,
center_ab.id,
center_ba.id,
group_aaa.id,
group_aba.id,
group_baa.id,
]
assert [x["name"] for x in resp.data["results"]] == [
"公司",
"部门A",
"部门B",
"中心AA",
"中心AB",
"中心BA",
"小组AAA",
"小组ABA",
"小组BAA",
]
assert [x["parent_id"] for x in resp.data["results"]] == [
None,
company.id,
company.id,
dept_a.id,
dept_a.id,
dept_b.id,
center_aa.id,
center_ab.id,
center_ba.id,
]
Loading