From 2e91001202cc3a12abd3e1d1cc7655606ce2b278 Mon Sep 17 00:00:00 2001 From: Timmy Date: Tue, 21 Nov 2023 17:20:54 +0800 Subject: [PATCH] feat: add instance approver node support (#2367) --- saas/backend/biz/application.py | 2 + saas/backend/biz/application_process.py | 92 ++++++++++++++++++++++++- saas/backend/service/constants.py | 1 + saas/backend/service/models/approval.py | 9 +++ 4 files changed, 103 insertions(+), 1 deletion(-) diff --git a/saas/backend/biz/application.py b/saas/backend/biz/application.py index d769075c9..752a619c6 100644 --- a/saas/backend/biz/application.py +++ b/saas/backend/biz/application.py @@ -70,6 +70,7 @@ from .application_process import ( GradeManagerApproverHandler, InstanceApproverHandler, + InstanceApproverMergeHandler, PolicyProcess, PolicyProcessHandler, ) @@ -510,6 +511,7 @@ def create_for_policy( # 5. 通过管道填充可能的资源实例审批人/分级管理员审批节点的审批人 pipeline: List[Type[PolicyProcessHandler]] = [ + InstanceApproverMergeHandler, InstanceApproverHandler, GradeManagerApproverHandler, ] diff --git a/saas/backend/biz/application_process.py b/saas/backend/biz/application_process.py index 208a47c9f..88c00eeda 100644 --- a/saas/backend/biz/application_process.py +++ b/saas/backend/biz/application_process.py @@ -76,7 +76,7 @@ def handle(self, policy_process_list: List[PolicyProcess]) -> List[PolicyProcess for policy_process in policy_process_list: # 没有实例审批人节点不需要处理 - if not policy_process.process.has_instance_approver_node(): + if not self.has_instance_approver_node(policy_process.process): policy_process_results.append(policy_process) continue @@ -103,6 +103,9 @@ def handle(self, policy_process_list: List[PolicyProcess]) -> List[PolicyProcess return policy_process_results + def has_instance_approver_node(self, process: ApprovalProcessWithNodeProcessor) -> bool: + return process.has_instance_approver_node() + def _split_policy_process_by_resource_approver_dict( self, policy_process: PolicyProcess, resource_approver_dict: ResourceNodeAttributeDictBean ) -> List[PolicyProcess]: @@ -184,6 +187,93 @@ def _list_approver_resource_node_by_policy(self, policy: PolicyBean) -> List[Res return list(resource_node_set) +class InstanceApproverMergeHandler(InstanceApproverHandler): + """ + 实例审批人合并审批 + """ + + def has_instance_approver_node(self, process: ApprovalProcessWithNodeProcessor) -> bool: + return process.has_instance_approver_merge_node() + + def _split_policy_process_by_resource_approver_dict( + self, policy_process: PolicyProcess, resource_approver_dict: ResourceNodeAttributeDictBean + ) -> List[PolicyProcess]: + """ + 通过实例审批人信息, 分离policy_process为独立的实例policy + """ + # 填充系统管理员, 默认为系统管理管理员 + if self.system_manager_approver: + policy_process.process.set_node_approver( + ProcessorNodeType.INSTANCE_APPROVER_MERGE.value, + self.system_manager_approver[0], + ) + + if len(policy_process.policy.list_thin_resource_type()) != 1: + return [policy_process] + + policy = policy_process.policy + copied_process = deepcopy(policy_process.process) + copied_process.set_node_approver( + ProcessorNodeType.INSTANCE_APPROVER_MERGE.value, + [], + ) + instance_approvers: List[str] = [] + + policy_process_list: List[PolicyProcess] = [] + for rg in policy.resource_groups: + rrt: RelatedResourceBean = rg.related_resource_types[0] # type: ignore + for condition in rrt.condition: + # 忽略有属性的condition + if not condition.has_no_attributes(): + continue + + # 遍历所有的实例路径, 筛选出有查询有实例审批人的实例 + for instance in condition.instances: + for path in instance.path: + last_node = path[-1] + if last_node.id == ANY_ID: + if len(path) < 2: + continue + last_node = path[-2] + + resource_node = ResourceNodeBean.parse_obj(last_node) + if not resource_approver_dict.get_attribute(resource_node): + continue + + # 由于ITSM的限制, 合并审批这里只取每个实例的第一个审批人 + instance_approvers.append(resource_approver_dict.get_attribute(resource_node)[0]) + + # 复制出单实例的policy + copied_policy = copy_policy_by_instance_path(policy, rg, rrt, instance, path) + policy_process_list.append(PolicyProcess(policy=copied_policy, process=copied_process)) + + # 如果没有拆分处理部分实例, 直接返回原始的policy_process + if not policy_process_list: + return [policy_process] + + # 填充实例审批人 + copied_process.set_node_approver( + ProcessorNodeType.INSTANCE_APPROVER_MERGE.value, + sorted(set(instance_approvers)), + ) + + # 把原始的策略剔除拆分的部分 + for part_policy_process in policy_process_list: + try: + policy_process.policy.remove_resource_group_list(part_policy_process.policy.resource_groups) + except PolicyEmptyException: + # 如果原始的策略全部删完了, 直接返回拆分的部分 + return policy_process_list + + # 原始拆分后剩余的部分填回来 + policy_process_list.append(policy_process) + return policy_process_list + + @cached_property + def system_manager_approver(self) -> List[str]: + return Role.objects.get(type=RoleType.SYSTEM_MANAGER.value, code=self.system_id).members + + def copy_policy_by_instance_path(policy, resource_group, rrt, instance, path): # 复制出单实例的policy return PolicyBean( diff --git a/saas/backend/service/constants.py b/saas/backend/service/constants.py index 161cd1a21..ea2ed7e0b 100644 --- a/saas/backend/service/constants.py +++ b/saas/backend/service/constants.py @@ -228,6 +228,7 @@ class ProcessorNodeType(LowerStrEnum): SYSTEM_MANAGER = auto() GRADE_MANAGER: enum = "rating_manager" INSTANCE_APPROVER = auto() + INSTANCE_APPROVER_MERGE = auto() # 每一种申请单据,对应的审批流程节点可以支持的ROLE diff --git a/saas/backend/service/models/approval.py b/saas/backend/service/models/approval.py index 22f5c7ed2..36a6ca409 100644 --- a/saas/backend/service/models/approval.py +++ b/saas/backend/service/models/approval.py @@ -148,3 +148,12 @@ def has_grade_manager_node(self) -> bool: if node.is_iam_source() and node.processor_type == ProcessorNodeType.GRADE_MANAGER.value: return True return False + + def has_instance_approver_merge_node(self) -> bool: + """ + 是否包含合并实例审批人节点 + """ + for node in self.nodes: + if node.is_iam_source() and node.processor_type == ProcessorNodeType.INSTANCE_APPROVER_MERGE.value: + return True + return False