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:批量续期支持不同过期时间 #2850

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
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
6 changes: 6 additions & 0 deletions saas/backend/api/management/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class ManagementAPIEnum(BaseAPIEnum):
V2_GROUP_MEMBER_ADD = auto()
V2_GROUP_MEMBER_DELETE = auto()
V2_GROUP_MEMBER_EXPIRED_AT_UPDATE = auto()
V2_GROUP_MEMBER_EXPIRED_AT_BATCH = auto()
V2_GROUP_SUBJECT_TEMPLATE_LIST = auto()
# 用户组权限
V2_GROUP_POLICY_GRANT = auto()
Expand All @@ -64,6 +65,7 @@ class ManagementAPIEnum(BaseAPIEnum):
# 用户组申请单
V2_GROUP_APPLICATION_CREATE = auto()
V2_GROUP_APPLICATION_RENEW = auto()
V2_GROUP_APPLICATION_RENEW_BATCH = auto()
# 用户组归属
V2_USER_GROUPS_BELONG_CHECK = auto()
V2_DEPARTMENT_GROUPS_BELONG_CHECK = auto()
Expand Down Expand Up @@ -125,6 +127,7 @@ class ManagementAPIEnum(BaseAPIEnum):
(V2_GROUP_MEMBER_ADD, "[V2]添加用户组成员"),
(V2_GROUP_MEMBER_DELETE, "[V2]删除用户组成员"),
(V2_GROUP_MEMBER_EXPIRED_AT_UPDATE, "[V2]用户组成员续期"),
(V2_GROUP_MEMBER_EXPIRED_AT_BATCH, "[V2]用户组成员批量续期"),
(V2_GROUP_SUBJECT_TEMPLATE_LIST, "[V2]获取用户组人员模板列表"),
# 用户组权限
(V2_GROUP_POLICY_GRANT, "[V2]授权用户组"),
Expand All @@ -134,6 +137,7 @@ class ManagementAPIEnum(BaseAPIEnum):
# 用户组申请单
(V2_GROUP_APPLICATION_CREATE, "[V2]创建用户组申请单"),
(V2_GROUP_APPLICATION_RENEW, "[V2]用户组续期申请单"),
(V2_GROUP_APPLICATION_RENEW_BATCH, "[V2]用户组批量续期申请单"),
# 用户组归属
(V2_USER_GROUPS_BELONG_CHECK, "[V2]判断用户与用户组归属"),
(V2_DEPARTMENT_GROUPS_BELONG_CHECK, "[V2]判断部门与用户组归属"),
Expand Down Expand Up @@ -168,6 +172,7 @@ class VerifyApiParamLocationEnum(ChoicesEnum, LowerStrEnum):
SYSTEM_IN_BODY = auto()
SYSTEM_IN_QUERY = auto()
GROUP_IDS_IN_BODY = auto()
GROUPS_IN_BODY = auto()
SYSTEM_IN_PATH = auto()
GROUP_IDS_IN_QUERY = auto()

Expand All @@ -178,6 +183,7 @@ class VerifyApiParamLocationEnum(ChoicesEnum, LowerStrEnum):
(SYSTEM_IN_BODY, "在body data里的system参数"),
(SYSTEM_IN_QUERY, "在get请求query里的system参数"),
(GROUP_IDS_IN_BODY, "在body data里的groups_ids参数"),
(GROUPS_IN_BODY, "在body data里的groups参数"),
(SYSTEM_IN_PATH, "在路径里的system参数"),
(GROUP_IDS_IN_QUERY, "在get请求query里的groups_ids参数"),
)
Expand Down
14 changes: 13 additions & 1 deletion saas/backend/api/management/v2/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
VerifyAPIParamSourceToObjectTypeMap,
)
from backend.api.management.mixins import ManagementAPIPermissionCheckMixin
from backend.api.management.v2.serializers import ManagementGroupIDsSLZ
from backend.api.management.v2.serializers import (
ManagementGroupIDsSLZ,
ManagementGroupApplicationBatchSLZ,
)
from backend.apps.role.models import Role, RoleRelatedObject, RoleSource
from backend.service.constants import RoleRelatedObjectType, RoleSourceType, RoleType

Expand Down Expand Up @@ -114,6 +117,15 @@ def _verify_api(self, view, request, param_source, api):
self._verify_api_by_groups(app_code, group_ids, api, system_id)
return

# 参数来自body data - groups
if param_source == VerifyApiParamLocationEnum.GROUPS_IN_BODY:
slz = ManagementGroupApplicationBatchSLZ(data=request.data)
slz.is_valid(raise_exception=True)
group_ids = [group["id"] for group in slz.validated_data["groups"]]

self._verify_api_by_groups(app_code, group_ids, api, system_id)
return

def _verify_api_by_role(self, app_code: str, role_id: int, api: ManagementAPIEnum, system_id: str):
"""
由于管理员API有很大一部分都是在需要在分级管理员角色下操作的
Expand Down
16 changes: 16 additions & 0 deletions saas/backend/api/management/v2/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,3 +415,19 @@ class ManagementSubjectTemplateSLZ(serializers.ModelSerializer):
class Meta:
model = SubjectTemplate
fields = ("id", "name", "description", "readonly", "source_group_id", "creator", "created_time")


class ManagementGroupBatchSLZ(ExpiredAtSLZ):
id = serializers.IntegerField(help_text="用户组 ID")


class ManagementGroupApplicationBatchSLZ(ReasonSLZ):
groups = serializers.ListField(
child=ManagementGroupBatchSLZ(label="用户组信息"))
applicant = serializers.CharField(label="申请者的用户名", max_length=32)
content_template = serializers.DictField(label="审批单内容模板", required=False,
allow_empty=True, default=dict)
group_content = serializers.DictField(label="审批单内容", required=False,
allow_empty=True, default=dict)
title_prefix = serializers.CharField(label="审批单标题前缀", required=False,
allow_blank=True, default="")
13 changes: 13 additions & 0 deletions saas/backend/api/management/v2/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@
views.ManagementGroupMemberExpiredAtViewSet.as_view({"put": "update"}),
name="open.management.v2.group_member.expired_at",
),
# 用户组成员有效期批量更新(不支持人员模板)
path(
"groups/<int:id>/members/batch/expired_at/",
views.ManagementGroupMemberExpiredAtViewSet.as_view({"post": "batch"}),
name="open.management.v2.group_member.batch_expired_at",
),
# 用户组下类型为人员模板的成员
path(
"groups/<int:id>/subject_templates/",
Expand Down Expand Up @@ -88,6 +94,13 @@
views.ManagementGroupRenewApplicationViewSet.as_view({"post": "create"}),
name="open.management.v2.group_renew_application",
),
# 用户组批量续期申请单
path(
"groups/-/batch/applications/",
views.ManagementGroupRenewApplicationViewSet.
as_view({"post": "batch"}),
name="open.management.v2.group_batch_renew_application",
),
# 用户组申请单
path(
"groups/-/applications/",
Expand Down
54 changes: 54 additions & 0 deletions saas/backend/api/management/v2/views/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
ManagementGradeManagerApplicationResultSLZ,
ManagementGradeManagerCreateApplicationSLZ,
ManagementGroupApplicationCreateSLZ,
ManagementGroupApplicationBatchSLZ,
)
from backend.apps.application.models import Application
from backend.apps.organization.models import User as UserModel
Expand Down Expand Up @@ -279,6 +280,10 @@ class ManagementGroupRenewApplicationViewSet(GenericViewSet):
VerifyApiParamLocationEnum.GROUP_IDS_IN_BODY.value,
ManagementAPIEnum.V2_GROUP_APPLICATION_RENEW.value,
),
"batch": (
VerifyApiParamLocationEnum.GROUPS_IN_BODY.value,
ManagementAPIEnum.V2_GROUP_APPLICATION_RENEW_BATCH.value,
),
}

biz = ApplicationBiz()
Expand Down Expand Up @@ -327,3 +332,52 @@ def create(self, request, *args, **kwargs):
)

return Response({"ids": [a.id for a in applications]})

@swagger_auto_schema(
operation_description="用户组批量续期申请单",
request_body=ManagementGroupApplicationBatchSLZ(label="用户组批量续期申请单"),
responses={status.HTTP_200_OK: ManagementApplicationIDSLZ(
label="单据ID列表")},
tags=["management.group.application"],
)
def batch(self, request, *args, **kwargs):
"""
用户组批量续期申请单,支持不同过期时间
"""
serializer = ManagementGroupApplicationBatchSLZ(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data

# 判断用户加入的用户组数与申请的数是否超过最大限制
user_id = data["applicant"]

# 转换为ApplicationBiz创建申请单所需数据结构
user = UserModel.objects.filter(username=user_id).first()
if not user:
raise (error_codes.INVALID_ARGS.
format(f"user: {user_id} not exists"))

source_system_id = kwargs["system_id"]

# 检查用户组数量是否超限
self.group_biz.check_subject_groups_quota(
Subject.from_username(user_id), data["groups"])

# 创建申请
applications = self.biz.create_for_group(
ApplicationType.RENEW_GROUP.value,
GroupApplicationDataBean(
applicant=user.username,
reason=data["reason"],
groups=parse_obj_as(
List[ApplicationGroupInfoBean],
data["groups"],
),
applicants=[Applicant(type=SubjectType.USER.value,
id=user.username,
display_name=user.display_name)],
),
source_system_id=source_system_id,
)

return Response({"ids": [a.id for a in applications]})
37 changes: 36 additions & 1 deletion saas/backend/api/management/v2/views/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@
GroupUpdateAuditProvider,
)
from backend.apps.group.models import Group
from backend.apps.group.serializers import GroupAddMemberSLZ
from backend.apps.group.serializers import (
GroupAddMemberSLZ,
GroupBatchUpdateMemberSLZ,
)
from backend.apps.group.views import split_members_to_subject_and_template
from backend.apps.policy.models import Policy
from backend.apps.policy.serializers import PolicySLZ
Expand Down Expand Up @@ -450,6 +453,10 @@ class ManagementGroupMemberExpiredAtViewSet(GenericViewSet):
VerifyApiParamLocationEnum.GROUP_IN_PATH.value,
ManagementAPIEnum.V2_GROUP_MEMBER_EXPIRED_AT_UPDATE.value,
),
"batch": (
VerifyApiParamLocationEnum.GROUP_IN_PATH.value,
ManagementAPIEnum.V2_GROUP_MEMBER_EXPIRED_AT_BATCH.value,
),
}

lookup_field = "id"
Expand Down Expand Up @@ -484,6 +491,34 @@ def update(self, request, *args, **kwargs):

return Response({})

@swagger_auto_schema(
operation_description="用户组成员有效期批量更新",
request_body=GroupBatchUpdateMemberSLZ(label="用户组成员"),
responses={status.HTTP_200_OK: serializers.Serializer()},
tags=["management.role.group.member"],
)
@view_audit_decorator(GroupMemberRenewAuditProvider)
def batch(self, request, *args, **kwargs):
group = self.get_object()

serializer = GroupBatchUpdateMemberSLZ(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data

members = [
GroupMemberExpiredAtBean(type=m["type"], id=m["id"],
expired_at=m["expired_at"])
for m in data["members"]
]

# 更新有效期
self.biz.update_members_expired_at(group.id, members)

# 写入审计上下文
audit_context_setter(group=group, members=data["members"])

return Response({})


class ManagementGroupSubjectTemplateViewSet(GenericViewSet):
"""用户组类型为人员模板的成员"""
Expand Down
26 changes: 26 additions & 0 deletions saas/backend/apps/group/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,3 +402,29 @@ def get_created_time(self, obj):

class SearchTemplateGroupMemberSLZ(SearchMemberSLZ):
template_id = serializers.IntegerField(label="模板ID", required=True)


class GroupMemberExpiredSLZ(GroupMemberSLZ):
expired_at = serializers.IntegerField(label="过期时间",
max_value=PERMANENT_SECONDS)


class GroupBatchUpdateMemberSLZ(serializers.Serializer):
members = serializers.ListField(label="成员列表",
child=GroupMemberExpiredSLZ(label="成员"),
allow_empty=False)

def validate_members(self, value):
members = []

for m in value:
# 过期时间不能小于当前时间
if m["expired_at"] <= int(time.time()):
raise (serializers.
ValidationError("expired_at must more then now"))
# 屏蔽admin授权
if not (m["type"] == GroupMemberType.USER.value
and m["id"] == ADMIN_USER):
members.append(m)

return members