Skip to content

Commit

Permalink
feature: 支持租户手机号/邮箱编辑
Browse files Browse the repository at this point in the history
  • Loading branch information
neronkl committed Sep 20, 2023
1 parent 2fb5d78 commit c995aa1
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 28 deletions.
33 changes: 33 additions & 0 deletions src/bk-user/bkuser/apis/web/person_center/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from bkuser.apis.web.organization.serializers import TenantUserDepartmentOutputSLZ, TenantUserLeaderOutputSLZ
from bkuser.apps.tenant.models import TenantUser
from bkuser.biz.tenant import TenantUserHandler
from bkuser.common.error_codes import error_codes
from bkuser.common.validators import validate_phone_with_country_code


class TenantBaseInfoOutputSLZ(serializers.Serializer):
Expand Down Expand Up @@ -97,3 +99,34 @@ def to_representation(self, instance: TenantUser) -> Dict:
}
)
return data


class TenantUserPhoneInputSLZ(serializers.Serializer):
is_inherited_phone = serializers.BooleanField(required=True, help_text="是否继承数据源手机号")
custom_phone = serializers.CharField(required=False, help_text="自定义用户手机号")
custom_phone_country_code = serializers.CharField(
required=False, help_text="自定义用户手机国际区号", default=settings.DEFAULT_PHONE_COUNTRY_CODE
)

def validate(self, attrs):
# custom_phone_country_code 默认为:86
# 通过继承,custom_phone 必须存在
if not attrs["is_inherited_phone"] and not attrs.get("custom_phone"):
raise error_codes.VALIDATION_ERROR.f("缺少参数:custom_phone")

# 校验手机号
validate_phone_with_country_code(phone=attrs["custom_phone"], country_code=attrs["custom_phone_country_code"])

return attrs


class TenantUserEmailInputSLZ(serializers.Serializer):
is_inherited_email = serializers.BooleanField(required=True, help_text="是否继承数据源邮箱")
custom_email = serializers.EmailField(required=False, help_text="自定义用户邮箱")

def validate(self, attrs):
# 通过继承,custom_email 必须存在
if not attrs["is_inherited_email"] and not attrs.get("custom_email"):
raise error_codes.VALIDATION_ERROR.f("缺少参数:custom_email")

return attrs
10 changes: 10 additions & 0 deletions src/bk-user/bkuser/apis/web/person_center/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,14 @@
),
# 租户用户详情
path("tenant-users/<str:id>/", views.TenantUserRetrieveApi.as_view(), name="person_center.tenant_users.retrieve"),
path(
"tenant-users/<str:id>/phone/",
views.TenantUserPhonePatchApi.as_view(),
name="person_center.tenant_users.phone.patch",
),
path(
"tenant-users/<str:id>/email/",
views.TenantUserEmailPatchApi.as_view(),
name="person_center.tenant_users.email.patch",
),
]
101 changes: 77 additions & 24 deletions src/bk-user/bkuser/apis/web/person_center/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
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 typing import List

from drf_yasg.utils import swagger_auto_schema
from rest_framework import generics, status
Expand All @@ -17,10 +16,13 @@
from bkuser.apis.web.mixins import CurrentUserTenantMixin
from bkuser.apis.web.person_center.serializers import (
NaturalUserWithTenantUserListOutputSLZ,
TenantUserEmailInputSLZ,
TenantUserPhoneInputSLZ,
TenantUserRetrieveOutputSLZ,
)
from bkuser.apps.tenant.models import TenantUser
from bkuser.biz.natural_user import NaturalUserWithTenantUsers, NatureUserHandler, TenantBaseInfo, TenantUserBaseInfo
from bkuser.biz.tenant import TenantUserHandler, TenantUserUpdateEmailInfo, TenantUserUpdatePhoneInfo
from bkuser.common.error_codes import error_codes


Expand All @@ -33,30 +35,23 @@ class NaturalUserTenantUserListApi(CurrentUserTenantMixin, generics.ListAPIView)
responses={status.HTTP_200_OK: NaturalUserWithTenantUserListOutputSLZ()},
)
def get(self, request, *args, **kwargs):
current_tenant_user = self.get_current_tenant_user()

# 获取当前登录的租户用户的自然人
# 未绑定自然人,则返回(伪)自然人=>租户用户的对应信息
current_tenant_user = self.get_current_tenant_user()
nature_user = NatureUserHandler.get_nature_user_by_tenant_user_id(current_tenant_user.id)

data_source_user_ids: List[int] = []

# 当前租户用户的数据源用户绑定了自然人,返回自然人绑定数据源用户
if nature_user is not None:
data_source_user_ids += nature_user.data_source_user_ids
else:
# 未绑定自然人,则返回当用户所属数据源用户
data_source_user_ids.append(current_tenant_user.data_source_user.id)

# 将当前登录置顶
# 通过比对租户用户id, 当等于当前登录用户的租户id,将其排序到查询集的顶部, 否则排序到查询集的底部
tenant_users = TenantUser.objects.filter(data_source_user_id__in=data_source_user_ids)
tenant_users = TenantUser.objects.select_related("data_source_user").filter(
data_source_user_id__in=nature_user.data_source_user_ids
)
sorted_tenant_users = sorted(tenant_users, key=lambda t: t.id != current_tenant_user.id)

# 响应数据组装
# 当前登录的用户,未绑定自然人,(伪)自然人为当前租户用户
data = NaturalUserWithTenantUsers(
id=nature_user.id if nature_user else current_tenant_user.id,
full_name=nature_user.full_name if nature_user else current_tenant_user.data_source_user.full_name,
id=nature_user.id,
full_name=nature_user.full_name,
tenant_users=[
TenantUserBaseInfo(
id=user.id,
Expand All @@ -82,22 +77,80 @@ class TenantUserRetrieveApi(CurrentUserTenantMixin, generics.RetrieveAPIView):
responses={status.HTTP_200_OK: TenantUserRetrieveOutputSLZ()},
)
def get(self, request, *args, **kwargs):
instance: TenantUser = self.get_object()

# 获取当前登录的租户用户的自然人
# 未绑定自然人,则返回(伪)自然人=>租户用户的对应信息
current_tenant_user = self.get_current_tenant_user()
nature_user = NatureUserHandler.get_nature_user_by_tenant_user_id(current_tenant_user.id)

# 边界限制
# 该租户用户的数据源用户,不属于当前自然人
if instance.data_source_user_id not in nature_user.data_source_user_ids:
raise error_codes.NO_PERMISSION

return Response(TenantUserRetrieveOutputSLZ(instance).data)


class TenantUserPhonePatchApi(CurrentUserTenantMixin, generics.UpdateAPIView):
queryset = TenantUser.objects.all()
lookup_url_kwarg = "id"

@swagger_auto_schema(
tags=["person_center"],
operation_description="租户用户更新手机号",
request_body=TenantUserPhoneInputSLZ,
responses={status.HTTP_200_OK: ""},
)
def patch(self, request, *args, **kwargs):
instance: TenantUser = self.get_object()

# 获取当前登录的租户用户的自然人
# 未绑定自然人,则返回(伪)自然人=>租户用户的对应信息
current_tenant_user = self.get_current_tenant_user()
nature_user = NatureUserHandler.get_nature_user_by_tenant_user_id(current_tenant_user.id)

data_source_user_ids: List[int] = []
# 边界限制
# 该租户用户的数据源用户,不属于当前自然人
if instance.data_source_user_id not in nature_user.data_source_user_ids:
raise error_codes.NO_PERMISSION

input_slz = TenantUserPhoneInputSLZ(data=request.data)
input_slz.is_valid(raise_exception=True)

phone_info = TenantUserUpdatePhoneInfo(**input_slz.validated_data)
TenantUserHandler.update_tenant_user_phone(instance, phone_info)

# 当前租户用户的数据源用户绑定了自然人,返回自然人绑定数据源用户
if nature_user is not None:
data_source_user_ids += nature_user.data_source_user_ids
else:
# 未绑定自然人,则返回当用户所属数据源用户
data_source_user_ids.append(current_tenant_user.data_source_user.id)
return Response()


class TenantUserEmailPatchApi(CurrentUserTenantMixin, generics.UpdateAPIView):
queryset = TenantUser.objects.all()
lookup_url_kwarg = "id"

@swagger_auto_schema(
tags=["person_center"],
operation_description="租户用户更新手机号",
request_body=TenantUserEmailInputSLZ,
responses={status.HTTP_200_OK: ""},
)
def patch(self, request, *args, **kwargs):
instance: TenantUser = self.get_object()
if instance.data_source_user_id not in data_source_user_ids:

# 获取当前登录的租户用户的自然人
# 未绑定自然人,则返回(伪)自然人=>租户用户的对应信息
current_tenant_user = self.get_current_tenant_user()
nature_user = NatureUserHandler.get_nature_user_by_tenant_user_id(current_tenant_user.id)

# 边界限制
# 该租户用户的数据源用户,不属于当前自然人
if instance.data_source_user_id not in nature_user.data_source_user_ids:
raise error_codes.NO_PERMISSION

return Response(TenantUserRetrieveOutputSLZ(instance).data)
input_slz = TenantUserEmailInputSLZ(data=request.data)
input_slz.is_valid(raise_exception=True)

email_info = TenantUserUpdateEmailInfo(**input_slz.validated_data)
TenantUserHandler.update_tenant_user_email(instance, email_info)

return Response()
13 changes: 9 additions & 4 deletions src/bk-user/bkuser/biz/natural_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
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 typing import List, Optional
from typing import List

from pydantic import BaseModel

Expand Down Expand Up @@ -43,7 +43,7 @@ class NaturalUserWithTenantUsers(BaseModel):

class NatureUserHandler:
@staticmethod
def get_nature_user_by_tenant_user_id(tenant_user_id: str) -> Optional[NaturalUserInfo]:
def get_nature_user_by_tenant_user_id(tenant_user_id: str) -> NaturalUserInfo:
"""
通过租户用户ID获取对应的自然人ID
"""
Expand All @@ -55,10 +55,15 @@ def get_nature_user_by_tenant_user_id(tenant_user_id: str) -> Optional[NaturalUs
data_source_user_id=tenant_user.data_source_user_id
).first()
if not natural_user_relation:
return None
# 未绑定自然人,则返回(伪)自然人=>租户用户对应信息
return NaturalUserInfo(
id=tenant_user.id,
full_name=tenant_user.data_source_user.full_name,
data_source_user_ids=[tenant_user.data_source_user_id],
)

# 未绑定自然人,则返回自然人信息
natural_user = natural_user_relation.natural_user

return NaturalUserInfo(
id=natural_user.id,
full_name=natural_user.full_name,
Expand Down
27 changes: 27 additions & 0 deletions src/bk-user/bkuser/biz/tenant.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from collections import defaultdict
from typing import Dict, List, Optional

from django.conf import settings
from django.db import transaction
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
Expand Down Expand Up @@ -90,6 +91,17 @@ class TenantUserLeaderInfo(BaseModel):
full_name: str


class TenantUserUpdatePhoneInfo(BaseModel):
is_inherited_phone: bool
custom_phone: Optional[str] = ""
custom_phone_country_code: Optional[str] = settings.DEFAULT_PHONE_COUNTRY_CODE


class TenantUserUpdateEmailInfo(BaseModel):
is_inherited_email: bool
custom_email: Optional[str] = ""


class TenantUserHandler:
@staticmethod
def list_tenant_user_by_id(tenant_user_ids: List[str]) -> List[TenantUserWithInheritedInfo]:
Expand Down Expand Up @@ -222,6 +234,21 @@ def get_tenant_user_ids_by_tenant(tenant_id: str) -> List[str]:
"id", flat=True
)

@staticmethod
def update_tenant_user_phone(tenant_user: TenantUser, phone_info: TenantUserUpdatePhoneInfo):
tenant_user.is_inherited_phone = phone_info.is_inherited_phone
if not phone_info.is_inherited_phone:
tenant_user.custom_phone = phone_info.custom_phone
tenant_user.custom_phone_country_code = phone_info.custom_phone_country_code
tenant_user.save()

@staticmethod
def update_tenant_user_email(tenant_user: TenantUser, email_info: TenantUserUpdateEmailInfo):
tenant_user.is_inherited_email = email_info.is_inherited_email
if not email_info.is_inherited_email:
tenant_user.custom_email = email_info.custom_email
tenant_user.save()


class TenantHandler:
@staticmethod
Expand Down

0 comments on commit c995aa1

Please sign in to comment.