From 327a84bbd8d299ed3a96d27b6712443a3ea9a0c0 Mon Sep 17 00:00:00 2001 From: zhangzhanwei Date: Tue, 21 Apr 2026 10:38:17 +0800 Subject: [PATCH] feat: Resource dependency and resource dependent dialog view --- apps/application/flow/tools.py | 3 +- apps/common/constants/permission_constants.py | 41 +- .../resource_mapping_serializers.py | 51 ++ .../sql/list_mapping_resource.sql | 49 ++ .../sql/list_mapping_resource_ee.sql | 52 ++ apps/system_manage/urls.py | 1 + apps/system_manage/views/resource_mapping.py | 36 +- ui/src/api/workspace/resource-mapping.ts | 25 + ui/src/components/resource_mapping/index.vue | 374 +++++++++- ui/src/locales/lang/en-US/views/system.ts | 2 + ui/src/locales/lang/zh-CN/views/system.ts | 2 + ui/src/locales/lang/zh-Hant/views/system.ts | 2 + .../permission/application/system-manage.ts | 259 ++----- ui/src/permission/application/workspace.ts | 651 +++++++++++------- ui/src/utils/permission/data.ts | 142 ++-- ui/src/views/application/index.vue | 19 +- 16 files changed, 1185 insertions(+), 524 deletions(-) create mode 100644 apps/system_manage/sql/list_mapping_resource.sql create mode 100644 apps/system_manage/sql/list_mapping_resource_ee.sql diff --git a/apps/application/flow/tools.py b/apps/application/flow/tools.py index 1c85e589caa..6cf0e8ed29f 100644 --- a/apps/application/flow/tools.py +++ b/apps/application/flow/tools.py @@ -845,7 +845,8 @@ async def anext_async(agen): }, 'KNOWLEDGE': {'search-knowledge-node': lambda n: n.get('properties').get('node_data').get('knowledge_id_list')}, 'APPLICATION': { - 'application-node': lambda n: [n.get('properties').get('node_data').get('application_id')] + 'application-node': lambda n: [n.get('properties').get('node_data').get('application_id')], + 'ai-chat-node': lambda n: [*(n.get('properties').get('node_data').get('application_ids') or [])], } } diff --git a/apps/common/constants/permission_constants.py b/apps/common/constants/permission_constants.py index cb8c8cb8b71..3add59e0b12 100644 --- a/apps/common/constants/permission_constants.py +++ b/apps/common/constants/permission_constants.py @@ -708,15 +708,15 @@ class PermissionConstants(Enum): parent_group=[WorkspaceGroup.KNOWLEDGE, UserGroup.KNOWLEDGE] ) KNOWLEDGE_BATCH_DELETE = Permission(group=Group.KNOWLEDGE, operate=Operate.BATCH_DELETE, - role_list=[RoleConstants.ADMIN, RoleConstants.USER], - resource_permission_group_list=[ResourcePermissionConst.KNOWLEDGE_MANGE], - parent_group=[WorkspaceGroup.KNOWLEDGE, UserGroup.KNOWLEDGE], - ) + role_list=[RoleConstants.ADMIN, RoleConstants.USER], + resource_permission_group_list=[ResourcePermissionConst.KNOWLEDGE_MANGE], + parent_group=[WorkspaceGroup.KNOWLEDGE, UserGroup.KNOWLEDGE], + ) KNOWLEDGE_BATCH_MOVE = Permission(group=Group.KNOWLEDGE, operate=Operate.BATCH_MOVE, - role_list=[RoleConstants.ADMIN, RoleConstants.USER], - resource_permission_group_list=[ResourcePermissionConst.KNOWLEDGE_MANGE], - parent_group=[WorkspaceGroup.KNOWLEDGE, UserGroup.KNOWLEDGE], - ) + role_list=[RoleConstants.ADMIN, RoleConstants.USER], + resource_permission_group_list=[ResourcePermissionConst.KNOWLEDGE_MANGE], + parent_group=[WorkspaceGroup.KNOWLEDGE, UserGroup.KNOWLEDGE], + ) KNOWLEDGE_RESOURCE_AUTHORIZATION = Permission( group=Group.KNOWLEDGE, operate=Operate.AUTH, role_list=[RoleConstants.ADMIN, RoleConstants.USER], resource_permission_group_list=[ResourcePermissionConst.KNOWLEDGE_MANGE], @@ -1055,21 +1055,26 @@ class PermissionConstants(Enum): parent_group=[WorkspaceGroup.APPLICATION, UserGroup.APPLICATION], ) APPLICATION_BATCH_DELETE = Permission(group=Group.APPLICATION, operate=Operate.BATCH_DELETE, - role_list=[RoleConstants.ADMIN, RoleConstants.USER], - resource_permission_group_list=[ResourcePermissionConst.APPLICATION_MANGE], - parent_group=[WorkspaceGroup.APPLICATION, UserGroup.APPLICATION], - ) + role_list=[RoleConstants.ADMIN, RoleConstants.USER], + resource_permission_group_list=[ResourcePermissionConst.APPLICATION_MANGE], + parent_group=[WorkspaceGroup.APPLICATION, UserGroup.APPLICATION], + ) APPLICATION_BATCH_MOVE = Permission(group=Group.APPLICATION, operate=Operate.BATCH_MOVE, - role_list=[RoleConstants.ADMIN, RoleConstants.USER], - resource_permission_group_list=[ResourcePermissionConst.APPLICATION_MANGE], - parent_group=[WorkspaceGroup.APPLICATION, UserGroup.APPLICATION], - ) + role_list=[RoleConstants.ADMIN, RoleConstants.USER], + resource_permission_group_list=[ResourcePermissionConst.APPLICATION_MANGE], + parent_group=[WorkspaceGroup.APPLICATION, UserGroup.APPLICATION], + ) APPLICATION_RESOURCE_AUTHORIZATION = Permission(group=Group.APPLICATION, operate=Operate.AUTH, role_list=[RoleConstants.ADMIN, RoleConstants.USER], parent_group=[WorkspaceGroup.APPLICATION, UserGroup.APPLICATION], resource_permission_group_list=[ ResourcePermissionConst.APPLICATION_MANGE], ) + APPLICATION_RELATE_RESOURCE_VIEW = Permission( + group=Group.APPLICATION, operate=Operate.RELATE_VIEW, role_list=[RoleConstants.ADMIN, RoleConstants.USER], + parent_group=[WorkspaceGroup.APPLICATION, UserGroup.APPLICATION], + resource_permission_group_list=[ResourcePermissionConst.APPLICATION_MANGE] + ) APPLICATION_TRIGGER_READ = Permission( group=Group.APPLICATION, operate=Operate.TRIGGER_READ, role_list=[RoleConstants.ADMIN, RoleConstants.USER], parent_group=[WorkspaceGroup.APPLICATION, UserGroup.APPLICATION], @@ -1592,6 +1597,10 @@ class PermissionConstants(Enum): group=Group.SYSTEM_RES_APPLICATION, operate=Operate.TRIGGER_DELETE, role_list=[RoleConstants.ADMIN], parent_group=[SystemGroup.RESOURCE_APPLICATION], is_ee=settings.edition == "EE" ) + RESOURCE_APPLICATION_RELATE_RESOURCE_VIEW = Permission( + group=Group.SYSTEM_RES_APPLICATION, operate=Operate.RELATE_VIEW, role_list=[RoleConstants.ADMIN], + parent_group=[SystemGroup.RESOURCE_APPLICATION], is_ee=settings.edition == "EE" + ) RESOURCE_APPLICATION_OVERVIEW_READ = Permission( group=Group.SYSTEM_RES_APPLICATION_OVERVIEW, operate=Operate.READ, role_list=[RoleConstants.ADMIN], parent_group=[SystemGroup.RESOURCE_APPLICATION], is_ee=settings.edition == "EE" diff --git a/apps/system_manage/serializers/resource_mapping_serializers.py b/apps/system_manage/serializers/resource_mapping_serializers.py index a3e61226a1d..1fe076da477 100644 --- a/apps/system_manage/serializers/resource_mapping_serializers.py +++ b/apps/system_manage/serializers/resource_mapping_serializers.py @@ -101,3 +101,54 @@ def get_resource_count(self, result_list): model['resource_count'] = count_dict.get(model_id, 0) return result_list + + +class MappingResourceSerializer(serializers.Serializer): + resource = serializers.CharField(required=True, label=_('resource')) + resource_id = serializers.UUIDField(required=True, label=_('resource Id')) + + resource_name = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('resource Name')) + target_type = serializers.ListField( + label=_('target Type'), + child=serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('target Type'))) + user_name = serializers.CharField(required=True, allow_null=True, allow_blank=True, label=_('creator')) + workspace_ids = serializers.CharField(required=False, label=_('workspace_ids')) + + def get_query_set(self): + queryset = QuerySet(model=get_dynamics_model({ + 'tdc.name': models.CharField(), + 'source_id': models.CharField(), + "source_type": models.CharField(), + "u.username": models.CharField(), + 'rm.target_type': models.CharField(), + 'workspace_id': models.CharField(), + })) + + queryset = queryset.filter(source_id=self.data.get('resource_id'), + source_type=self.data.get('resource')) + + if self.data.get('resource_name'): + queryset = queryset.filter(**{'tdc.name__icontains': self.data.get('resource_name')}) + if self.data.get('user_name'): + queryset = queryset.filter(**{'u.username__icontains': self.data.get('user_name')}) + if self.data.get("target_type"): + queryset = queryset.filter(**{'rm.target_type__in': self.data.get('target_type')}) + if self.data.get('workspace_ids') is not None and len(self.data.get('workspace_ids')) > 0: + workspace_ids = json.loads(self.data.get('workspace_ids')) + queryset = queryset.filter(**{'workspace_id__in': workspace_ids}) + + return queryset + + @staticmethod + def is_x_pack_ee(): + workspace_model = DatabaseModelManage.get_model("workspace_model") + return workspace_model is not None + + def page(self, current_page, page_size): + is_x_pack_ee = self.is_x_pack_ee() + + return native_page_search(current_page, page_size, self.get_query_set(), get_file_content( + os.path.join(PROJECT_DIR, "apps", "system_manage", + 'sql', 'list_mapping_resource_ee.sql' if is_x_pack_ee else 'list_mapping_resource.sql')), + with_table_name=False + ) diff --git a/apps/system_manage/sql/list_mapping_resource.sql b/apps/system_manage/sql/list_mapping_resource.sql new file mode 100644 index 00000000000..6151c18b976 --- /dev/null +++ b/apps/system_manage/sql/list_mapping_resource.sql @@ -0,0 +1,49 @@ +WITH target_data_cte AS (SELECT 'APPLICATION' as target_type, + id, + "name", + "desc", + "user_id", + "workspace_id", + "icon", + "type", + "folder_id" + FROM application + UNION ALL + SELECT 'KNOWLEDGE' as target_type, + id, + "name", + "desc", + "user_id", + "workspace_id", + "type"::text as "icon" , "type"::text as "type", "folder_id" + FROM knowledge + UNION ALL + SELECT 'TOOL' as target_type, + id, + "name", + "desc", + "user_id", + "workspace_id", + "icon", + "tool_type" as "type", + "folder_id" + FROM tool + UNION ALL + SELECT 'MODEL' as target_type, + id, + "name", + ''::text as "desc", "user_id", + "workspace_id", + "provider" as "icon", + "model_type" as "type", + ''::text as "folder_id" + FROM model) +SELECT rm.*, + tdc.*, + u.nick_name as username +FROM resource_mapping rm + LEFT JOIN target_data_cte tdc + ON rm.target_type = tdc.target_type + AND rm.target_id::uuid = tdc.id +LEFT JOIN "public"."user" u +ON u.id = tdc.user_id \ No newline at end of file diff --git a/apps/system_manage/sql/list_mapping_resource_ee.sql b/apps/system_manage/sql/list_mapping_resource_ee.sql new file mode 100644 index 00000000000..62694316169 --- /dev/null +++ b/apps/system_manage/sql/list_mapping_resource_ee.sql @@ -0,0 +1,52 @@ +WITH target_data_cte AS (SELECT 'APPLICATION' as target_type, + id, + "name", + "desc", + "user_id", + "workspace_id", + "icon", + "type", + "folder_id" + FROM application + UNION ALL + SELECT 'KNOWLEDGE' as target_type, + id, + "name", + "desc", + "user_id", + "workspace_id", + "type"::text as "icon" , "type"::text as "type", "folder_id" + FROM knowledge + UNION ALL + SELECT 'TOOL' as target_type, + id, + "name", + "desc", + "user_id", + "workspace_id", + "icon", + "tool_type" as "type", + "folder_id" + FROM tool + UNION ALL + SELECT 'MODEL' as target_type, + id, + "name", + ''::text as "desc", "user_id", + "workspace_id", + "provider" as "icon", + "model_type" as "type", + ''::text as "folder_id" + FROM model) +SELECT rm.*, + tdc.*, + u.nick_name as username, + w.name as workspace_name +FROM resource_mapping rm + LEFT JOIN target_data_cte tdc + ON rm.target_type = tdc.target_type + AND rm.target_id::uuid = tdc.id +LEFT JOIN "public"."user" u +ON u.id = tdc.user_id + LEFT JOIN "public"."workspace" w + ON w.id = tdc.workspace_id \ No newline at end of file diff --git a/apps/system_manage/urls.py b/apps/system_manage/urls.py index 2938e6dbdd3..a60f9a5e297 100644 --- a/apps/system_manage/urls.py +++ b/apps/system_manage/urls.py @@ -10,6 +10,7 @@ path('workspace//resource_user_permission/resource//resource/', views.WorkspaceResourceUserPermissionView.as_view()), path('workspace//resource_user_permission/resource//resource///', views.WorkspaceResourceUserPermissionView.Page.as_view()), path('workspace//resource_mapping////', views.ResourceMappingView.as_view()), + path('workspace//mapping_resource////', views.MappingResourceView.as_view()), path('email_setting', views.SystemSetting.Email.as_view()), path('profile', views.SystemProfile.as_view()), path('valid//', views.Valid.as_view()) diff --git a/apps/system_manage/views/resource_mapping.py b/apps/system_manage/views/resource_mapping.py index 13ebc9e9adf..ff3c71d303d 100644 --- a/apps/system_manage/views/resource_mapping.py +++ b/apps/system_manage/views/resource_mapping.py @@ -18,7 +18,7 @@ from common.constants.permission_constants import Permission, Group, Operate, RoleConstants, ViewPermission, \ CompareConstants from system_manage.api.resource_mapping import ResourceMappingAPI -from system_manage.serializers.resource_mapping_serializers import ResourceMappingSerializer +from system_manage.serializers.resource_mapping_serializers import ResourceMappingSerializer, MappingResourceSerializer class ResourceMappingView(APIView): @@ -53,3 +53,37 @@ def get(self, request: Request, workspace_id: str, resource: str, resource_id: s 'user_name': request.query_params.get('user_name'), 'source_type': request.query_params.getlist('source_type[]'), }).page(current_page, page_size)) + + +class MappingResourceView(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_('Retrieve the pagination list of resource relationships'), + operation_id=_('Retrieve the pagination list of resource relationships'), # type: ignore + responses=ResourceMappingAPI.get_response(), + parameters=ResourceMappingAPI.get_parameters(), + tags=[_('Mapping Resource')] # type: ignore + ) + @has_permissions( + lambda r, kwargs: Permission(group=Group(kwargs.get('resource')), + operate=Operate.RELATE_VIEW, + resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}:ROLE/WORKSPACE_MANAGE"), + lambda r, kwargs: Permission(group=Group(kwargs.get('resource')), + operate=Operate.RELATE_VIEW, + resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}/{kwargs.get('resource')}/{kwargs.get('resource_id')}"), + ViewPermission([RoleConstants.USER.get_workspace_role()], + [lambda r, kwargs: Permission(group=Group(kwargs.get('resource')), + operate=Operate.SELF, + resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}/{kwargs.get('resource')}/{kwargs.get('resource_id')}")], + CompareConstants.AND), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role()) + def get(self, request: Request, workspace_id: str, resource: str, resource_id: str, current_page, page_size): + return result.success(MappingResourceSerializer({ + 'resource': resource, + 'resource_id': resource_id, + 'resource_name': request.query_params.get('resource_name'), + 'user_name': request.query_params.get('user_name'), + 'target_type': request.query_params.getlist('target_type[]'), + }).page(current_page, page_size)) \ No newline at end of file diff --git a/ui/src/api/workspace/resource-mapping.ts b/ui/src/api/workspace/resource-mapping.ts index aedd8965ee5..98e74bc67b9 100644 --- a/ui/src/api/workspace/resource-mapping.ts +++ b/ui/src/api/workspace/resource-mapping.ts @@ -22,7 +22,32 @@ const getResourceMapping: ( loading, ) } +/** + * 依赖项 + * @param workspace_id + * @param resource + * @param resource_id + * @param page + * @param params + * @param loading + * @returns + */ +const getMappingResource: ( + workspace_id: string, + resource: string, + resource_id: string, + page: pageRequest, + params?: any, + loading?: Ref, +) => Promise> = (workspace_id, resource, resource_id, page, params, loading) => { + return get( + `${prefix}/${workspace_id}/mapping_resource/${resource}/${resource_id}/${page.current_page}/${page.page_size}`, + params, + loading, + ) +} export default { getResourceMapping, + getMappingResource, } diff --git a/ui/src/components/resource_mapping/index.vue b/ui/src/components/resource_mapping/index.vue index 6bf6c6c979a..afd0d6f47c7 100644 --- a/ui/src/components/resource_mapping/index.vue +++ b/ui/src/components/resource_mapping/index.vue @@ -15,6 +15,15 @@ :size="24" :type="currentSource.type" /> + + + - + + + + + + + + + + + + + + + + + + + - + - + + + {{ row.name }} @@ -142,7 +339,8 @@ show-overflow-tooltip :label="$t('common.type')" > -