Skip to content
Merged
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 apps/locales/en_US/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8886,4 +8886,10 @@ msgid "Role IDs cannot be empty"
msgstr ""

msgid "Some roles do not exist"
msgstr ""

msgid "Authorized pagination list for obtaining resources"
msgstr ""

msgid "Resources mapping"
msgstr ""
6 changes: 6 additions & 0 deletions apps/locales/zh_CN/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -9013,3 +9013,9 @@ msgstr "角色 ID 不能为空"

msgid "Some roles do not exist"
msgstr "部分角色不存在"

msgid "Authorized pagination list for obtaining resources"
msgstr "获取资源的关系分页列表"

msgid "Resources mapping"
msgstr "资源映射"
6 changes: 6 additions & 0 deletions apps/locales/zh_Hant/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -9013,3 +9013,9 @@ msgstr "角色 ID 不能为空"

msgid "Some roles do not exist"
msgstr "部分角色不存在"

msgid "Authorized pagination list for obtaining resources"
msgstr "獲取資源的關係分頁清單"

msgid "Resources mapping"
msgstr "資源映射"
80 changes: 80 additions & 0 deletions apps/system_manage/api/resource_mapping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# coding=utf-8
"""
@project: MaxKB
@Author:虎虎
@file: resource_mapping.py
@date:2025/12/26 14:07
@desc:
"""
from django.utils.translation import gettext_lazy as _
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter
from rest_framework import serializers

from common.mixins.api_mixin import APIMixin


class ResourceMappingResponse(serializers.Serializer):
id = serializers.UUIDField(required=True, label="主键id")
target_id = serializers.CharField(required=True, label="被关联资源名称")
target_type = serializers.CharField(required=True, label="被关联资源类型")
source_id = serializers.CharField(required=True, label="关联资源Id")
source_type = serializers.CharField(required=True, label="关联资源类型")
name = serializers.CharField(required=True, label="名称")
desc = serializers.CharField(required=False, label="描述")
user_id = serializers.UUIDField(required=True, label="主键id")


class ResourceMappingAPI(APIMixin):

@staticmethod
def get_parameters():
return [
OpenApiParameter(
name="workspace_id",
description="工作空间id",
type=OpenApiTypes.STR,
location='path',
required=True,
),
OpenApiParameter(
name="source",
description="资源类型",
type=OpenApiTypes.STR,
location='path',
required=True,
),
OpenApiParameter(
name="source_id",
description="资源id",
type=OpenApiTypes.STR,
location='path',
required=True,
),
OpenApiParameter(
name="current_page",
description=_("Current page"),
type=OpenApiTypes.INT,
location='path',
required=True,
),
OpenApiParameter(
name="page_size",
description=_("Page size"),
type=OpenApiTypes.INT,
location='path',
required=True,
),
OpenApiParameter(
name="resource_name",
description="名称",
type=OpenApiTypes.STR,
location='query',
required=False
),

]

@staticmethod
def get_response():
return ResourceMappingResponse(many=True)
44 changes: 44 additions & 0 deletions apps/system_manage/serializers/resource_mapping_serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# coding=utf-8
"""
@project: MaxKB
@Author:虎虎
@file: workspace_user_resource_permission.py
@date:2025/4/28 17:17
@desc:
"""
import os

from django.db import models
from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers

from common.db.search import native_page_search, get_dynamics_model
from common.utils.common import get_file_content
from maxkb.conf import PROJECT_DIR


class ResourceMappingSerializer(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'))

def get_query_set(self):
queryset = QuerySet(model=get_dynamics_model({
'name': models.CharField(),
'target_id': models.CharField(),
"target_type": models.CharField()
}))

queryset = queryset.filter(target_id=self.data.get('resource_id'),
target_type=self.data.get('resource'))

if self.data.get('resource_name'):
queryset = queryset.filter(name__icontains=self.data.get('resource_name'))

return queryset

def page(self, current_page, page_size):
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_resource_mapping.sql')), with_table_name=False)
12 changes: 12 additions & 0 deletions apps/system_manage/sql/list_resource_mapping.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
WITH source_data_cte AS (
SELECT 'APPLICATION' as source_type, id, "name", "desc","user_id"
FROM application
UNION ALL
SELECT 'KNOWLEDGE' as source_type, id, "name", "desc","user_id"
FROM knowledge)
SELECT rm.*,
sdc.*
FROM resource_mapping rm
LEFT JOIN source_data_cte sdc
ON rm.source_type = sdc.source_type
AND rm.source_id::uuid = sdc.id
1 change: 1 addition & 0 deletions apps/system_manage/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
path('workspace/<str:workspace_id>/user_resource_permission/user/<str:user_id>/resource/<str:resource>/<int:current_page>/<int:page_size>', views.WorkSpaceUserResourcePermissionView.Page.as_view()),
path('workspace/<str:workspace_id>/resource_user_permission/resource/<str:target>/resource/<str:resource>', views.WorkspaceResourceUserPermissionView.as_view()),
path('workspace/<str:workspace_id>/resource_user_permission/resource/<str:target>/resource/<str:resource>/<int:current_page>/<int:page_size>', views.WorkspaceResourceUserPermissionView.Page.as_view()),
path('workspace/<str:workspace_id>/resource_mapping/<str:resource>/<str:resource_id>/<int:current_page>/<int:page_size>', views.ResourceMappingView.as_view()),
path('email_setting', views.SystemSetting.Email.as_view()),
path('profile', views.SystemProfile.as_view()),
path('valid/<str:valid_type>/<int:valid_count>', views.Valid.as_view())
Expand Down
1 change: 1 addition & 0 deletions apps/system_manage/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
from .email_setting import *
from .system_profile import *
from .valid import *
from .resource_mapping import *
37 changes: 37 additions & 0 deletions apps/system_manage/views/resource_mapping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# coding=utf-8
"""
@project: MaxKB
@Author:虎虎
@file: resource_mapping.py
@date:2025/12/25 15:28
@desc:
"""

from django.utils.translation import gettext_lazy as _
from drf_spectacular.utils import extend_schema
from rest_framework.request import Request
from rest_framework.views import APIView

from common import result
from common.auth import TokenAuth
from system_manage.api.resource_mapping import ResourceMappingAPI
from system_manage.serializers.resource_mapping_serializers import ResourceMappingSerializer


class ResourceMappingView(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=[_('Resources mapping')] # type: ignore
)
def get(self, request: Request, workspace_id: str, resource: str, resource_id: str, current_page, page_size):
return result.success(ResourceMappingSerializer({
'resource': resource,
'resource_id': resource_id,
'resource_name': request.query_params.get('resource_name')
}).page(current_page, page_size))
28 changes: 28 additions & 0 deletions ui/src/api/workspace/resource-mapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Result } from '@/request/Result'
import { get, put, post, del } from '@/request/index'
import type { Ref } from 'vue'
import type { pageRequest } from '@/api/type/common'
const prefix = '/workspace'

/**
* 工作空间下各个资源的映射关系
* @query 参数
*/
const getResourceMapping: (
workspace_id: string,
resource: string,
resource_id: string,
page: pageRequest,
params?: any,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (workspace_id, resource, resource_id, page, params, loading) => {
return get(
`${prefix}/${workspace_id}/resource_mapping/${resource}/${resource_id}/${page.current_page}/${page.page_size}`,
params,
loading,
)
}

export default {
getResourceMapping,
}
121 changes: 121 additions & 0 deletions ui/src/components/resource_mapping/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<template>
<el-drawer
v-model="visible"
:title="$t('views.system.resourceAuthorization.title')"
size="60%"
:append-to-body="true"
>
<div class="flex-between mb-16">
<div class="flex-between complex-search">
<el-select class="complex-search__left" v-model="searchType" style="width: 100px">
<el-option
:label="$t('views.userManage.userForm.resourceName.label')"
value="resource_name"
/>
</el-select>
<el-input
v-if="searchType === 'resource_name'"
v-model="query.resource_name"
:placeholder="$t('common.search')"
style="width: 220px"
clearable
@keyup.enter="pageResouceMapping()"
/>
</div>
</div>

<app-table
ref="multipleTableRef"
class="mt-16"
:data="tableData"
:pagination-config="paginationConfig"
@sizeChange="handleSizeChange"
@changePage="pageResouceMapping"
:maxTableHeight="200"
:row-key="(row: any) => row.id"
v-loading="loading"
>
<el-table-column
prop="name"
:label="$t('views.userManage.userForm.name.label', '名称')"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
prop="desc"
min-width="120"
show-overflow-tooltip
:label="$t('views.login.loginForm.desc.label', '描述')"
/>
</app-table>
</el-drawer>
</template>
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
import { useRoute } from 'vue-router'
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
import useStore from '@/stores'
const route = useRoute()
const { user } = useStore()
const searchType = ref<string>('resource_name')
const query = ref<any>({
resource_name: '',
})
const loading = ref<boolean>(false)
const tableData = ref<Array<any>>()
const visible = ref<boolean>(false)
const paginationConfig = reactive({
current_page: 1,
page_size: 20,
total: 0,
})
const apiType = computed(() => {
if (route.path.includes('resource-management')) {
return 'systemManage'
} else {
return 'workspace'
}
})

const pageResouceMapping = () => {
const workspaceId = user.getWorkspaceId() || 'default'
const params: any = {}
if (query.value[searchType.value]) {
params[searchType.value] = query.value[searchType.value]
}
loadSharedApi({ type: 'resourceMapping', systemType: apiType.value })
.getResourceMapping(
workspaceId,
currentSource.value,
currentSourceId.value,
paginationConfig,
params,
loading,
)
.then((res: any) => {
tableData.value = res.data.records || []
paginationConfig.total = res.data.total || 0
})
}
function handleSizeChange() {
paginationConfig.current_page = 1
pageResouceMapping()
}
const currentSource = ref<string>()
const currentSourceId = ref<string>()
const open = (source: string, sourceId: string) => {
visible.value = true
currentSource.value = source
currentSourceId.value = sourceId
pageResouceMapping()
}
const close = () => {
visible.value = false
paginationConfig.current_page = 1
}
defineExpose({
open,
close,
})
</script>
<style lang="scss" scoped></style>
3 changes: 3 additions & 0 deletions ui/src/utils/dynamics-api/shared-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import knowledgeWorkspaceApi from '@/api/knowledge/knowledge'
import documentWorkspaceApi from '@/api/knowledge/document'
import paragraphWorkspaceApi from '@/api/knowledge/paragraph'
import problemWorkspaceApi from '@/api/knowledge/problem'
import resourceMappingApi from '@/api/workspace/resource-mapping'
import modelWorkspaceApi from '@/api/model/model'
import toolWorkspaceApi from '@/api/tool/tool'
import chatUserWorkspaceApi from '@/api/chat-user/chat-user'
Expand Down Expand Up @@ -34,6 +35,7 @@ import workflowVersionResourceApi from '@/api/system-resource-management/workflo
import chatLogResourceApi from '@/api/system-resource-management/chat-log'
import resourceAuthorizationResourceApi from '@/api/system-resource-management/resource-authorization'
import folderResourceApi from '@/api/system-resource-management/folder'

// 普通 API
const workspaceApiMap = {
knowledge: knowledgeWorkspaceApi,
Expand All @@ -50,6 +52,7 @@ const workspaceApiMap = {
chatLog: chatLogWorkspaceApi,
resourceAuthorization: resourceAuthorizationWorkspaceApi,
folder: folderWorkspaceApi,
resourceMapping: resourceMappingApi,
} as any

// 系统分享 API
Expand Down
Loading
Loading