diff --git a/apps/system_manage/serializers/resource_chat_user_group.py b/apps/system_manage/serializers/resource_chat_user_group.py
index be593e5df94..8a3b051a219 100644
--- a/apps/system_manage/serializers/resource_chat_user_group.py
+++ b/apps/system_manage/serializers/resource_chat_user_group.py
@@ -9,7 +9,7 @@
from common.exception.app_exception import AppApiException
from common.utils.common import get_file_content
from maxkb.conf import PROJECT_DIR
-from system_manage.models import ResourceChatUserGroupAuthorize, UserGroup
+from system_manage.models import ResourceChatUserAuthorize, ResourceChatUserGroupAuthorize, UserGroup
class ResourceChatUserGroupQuerySerializer(serializers.Serializer):
@@ -29,6 +29,9 @@ def get_query_set(self):
resource_chat_user_group_authorize_query_set = QuerySet(ResourceChatUserGroupAuthorize).filter(
resource_id=resource_id, resource_type=resource_type
)
+ resource_chat_user_authorize_query_set = QuerySet(ResourceChatUserAuthorize).filter(
+ resource_id=resource_id, resource_type=resource_type
+ )
query_set = QuerySet(UserGroup)
if user_group_name is not None:
query_set = query_set.filter(name__contains=user_group_name)
@@ -36,13 +39,20 @@ def get_query_set(self):
resource_chat_user_group_authorize_query_set = resource_chat_user_group_authorize_query_set.filter(
workspace_id=workspace_id
)
+ resource_chat_user_authorize_query_set = resource_chat_user_authorize_query_set.filter(
+ workspace_id=workspace_id
+ )
else:
resource_chat_user_group_authorize_query_set = resource_chat_user_group_authorize_query_set.filter(
workspace_id__isnull=True
)
+ resource_chat_user_authorize_query_set = resource_chat_user_authorize_query_set.filter(
+ workspace_id__isnull=True
+ )
return {
"default_query_set": query_set,
"resource_chat_user_group_authorize_query_set": resource_chat_user_group_authorize_query_set,
+ "resource_chat_user_authorize_query_set": resource_chat_user_authorize_query_set,
}
def list(self):
diff --git a/apps/system_manage/sql/list_resource_chat_user_group.sql b/apps/system_manage/sql/list_resource_chat_user_group.sql
index 09f141e3ba3..00308e2941c 100644
--- a/apps/system_manage/sql/list_resource_chat_user_group.sql
+++ b/apps/system_manage/sql/list_resource_chat_user_group.sql
@@ -1,5 +1,12 @@
-SELECT user_group.*, COALESCE(resource_chat_user_group_authorize.is_auth,false) as is_auth
+SELECT user_group.*, COALESCE(resource_chat_user_group_authorize.is_auth,false) as is_auth,
+ (SELECT COUNT(DISTINCT resource_chat_user_authorize.user_id)
+ FROM user_group_relation
+ JOIN (SELECT * FROM resource_chat_user_authorize ${resource_chat_user_authorize_query_set}) resource_chat_user_authorize
+ ON resource_chat_user_authorize.user_id = user_group_relation.user_id
+ AND resource_chat_user_authorize.user_group_id = user_group_relation.group_id
+ WHERE user_group_relation.group_id = user_group.id
+ AND resource_chat_user_authorize.is_auth = true) AS authorized_count
FROM "user_group"
LEFT JOIN (SELECT * FROM resource_chat_user_group_authorize ${resource_chat_user_group_authorize_query_set}) resource_chat_user_group_authorize
ON user_group.id = resource_chat_user_group_authorize.user_group_id
-${default_query_set}
\ No newline at end of file
+${default_query_set}
diff --git a/ui/src/api/types/chat-user.ts b/ui/src/api/types/chat-user.ts
index f84207538c7..4944e6eb3cd 100644
--- a/ui/src/api/types/chat-user.ts
+++ b/ui/src/api/types/chat-user.ts
@@ -112,6 +112,8 @@ export interface ChatUserAuthorizationGroup {
id: string
name: string
is_auth: boolean
+ /** 用户组内已授权对话用户数。 */
+ authorized_count: number
}
/** 用户组内的对话用户资源授权状态。 */
diff --git a/ui/src/views/VIEW_README.md b/ui/src/views/VIEW_README.md
index ed1c970b1af..f848a814677 100644
--- a/ui/src/views/VIEW_README.md
+++ b/ui/src/views/VIEW_README.md
@@ -1389,7 +1389,7 @@ API 地址、调用示例及 MCP 配置只读;后两个展示区参考工具 M
使用对应路由 ID 及 `RESOURCE_TYPE.APPLICATION / KNOWLEDGE`;授权类型必须大写,与后台授权记录及对话端查询一致。
两页通过 `hideHeader: true` 隐藏详情标题栏,使用普通 Flex 双栏容器,不嵌套 `MkViewLayout`。
外层 `ResourceDetailLayout` 统一管理整体滚动;侧栏复用 `w-sidebar-expanded`、`MkSearchList` 的搜索、选中和空态。
-“按用户组”列表通过插槽展示名称及自动授权状态;接口尚未返回组已授权人数,手动授权组显示“已授权 —”,不以当前页勾选数代替。
+“按用户组”列表通过插槽展示名称及自动授权状态;接口返回组已授权人数 `authorized_count`,自动授权组显示“(自动授权)”,手动授权组显示“(已授权 N)”,不以当前页勾选数代替。
右侧第一行展示组名和用户查询总数;下方工具栏左侧为默认按姓名查询的 `MkComplexSearch` 及自动授权开关,右侧为保存。
顶部 `MkComplexSearch` 按姓名、用户名排列;用户来源只在表头使用 `MkTableFilter mode="single"` 筛选,向接口传 `source`。
用户名、姓名搜索与表头来源可组合查询,清空一处条件不影响另一处,不额外重置搜索组件。
diff --git a/ui/src/views/application-detail/chat-user/index.vue b/ui/src/views/application-detail/chat-user/index.vue
index e11600916a0..73f21de9d90 100644
--- a/ui/src/views/application-detail/chat-user/index.vue
+++ b/ui/src/views/application-detail/chat-user/index.vue
@@ -39,6 +39,15 @@ function loadChatUserGroups() {
})
}
+// 仅刷新用户组列表及已授权人数,保留当前选中的用户组。
+function refreshChatUserGroups() {
+ return ChatUserApi.getUserGroupList(resource.value).then((groups) => {
+ const currentId = currentGroup.value?.id
+ chatUserGroups.value = groups
+ currentGroup.value = groups.find((group) => group.id === currentId) ?? groups[0]
+ })
+}
+
function handleSelectGroup(group: ChatUserAuthorizationGroup) {
if (loading.value || currentGroup.value?.id === group.id) return
currentGroup.value = group
@@ -130,7 +139,7 @@ function handleSave() {
// 先记住已保存结果,列表刷新失败时不重复提示未保存。
savedAuthorization.value = Object.fromEntries(payload.map(({ chat_user_id, is_auth }) => [chat_user_id, is_auth]))
MsgSuccess('保存成功')
- return loadChatUsers()
+ return loadChatUsers().then(refreshChatUserGroups)
})
.finally(() => {
loading.value = false
@@ -149,7 +158,7 @@ function handleAutoAuthorizationChange(value: string | number | boolean) {
chatUsers.value = []
savedAuthorization.value = {}
MsgSuccess('保存成功')
- return loadChatUsers()
+ return loadChatUsers().then(refreshChatUserGroups)
})
.finally(() => {
loading.value = false
@@ -170,8 +179,7 @@ onMounted(() => loadChatUserGroups())
{{ row.name }}
-
- {{ row.is_auth ? '(自动授权)' : '(已授权 —)' }}
+ {{ row.is_auth ? '(自动授权)' : `(已授权 ${row.authorized_count ?? 0})` }}
diff --git a/ui/src/views/knowledge-detail/chat-user/index.vue b/ui/src/views/knowledge-detail/chat-user/index.vue
index 898e02dc570..ec811515a7d 100644
--- a/ui/src/views/knowledge-detail/chat-user/index.vue
+++ b/ui/src/views/knowledge-detail/chat-user/index.vue
@@ -39,6 +39,15 @@ function loadChatUserGroups() {
})
}
+// 仅刷新用户组列表及已授权人数,保留当前选中的用户组。
+function refreshChatUserGroups() {
+ return ChatUserApi.getUserGroupList(resource.value).then((groups) => {
+ const currentId = currentGroup.value?.id
+ chatUserGroups.value = groups
+ currentGroup.value = groups.find((group) => group.id === currentId) ?? groups[0]
+ })
+}
+
function handleSelectGroup(group: ChatUserAuthorizationGroup) {
if (loading.value || currentGroup.value?.id === group.id) return
currentGroup.value = group
@@ -130,7 +139,7 @@ function handleSave() {
// 先记住已保存结果,列表刷新失败时不重复提示未保存。
savedAuthorization.value = Object.fromEntries(payload.map(({ chat_user_id, is_auth }) => [chat_user_id, is_auth]))
MsgSuccess('保存成功')
- return loadChatUsers()
+ return loadChatUsers().then(refreshChatUserGroups)
})
.finally(() => {
loading.value = false
@@ -149,7 +158,7 @@ function handleAutoAuthorizationChange(value: string | number | boolean) {
chatUsers.value = []
savedAuthorization.value = {}
MsgSuccess('保存成功')
- return loadChatUsers()
+ return loadChatUsers().then(refreshChatUserGroups)
})
.finally(() => {
loading.value = false
@@ -170,8 +179,7 @@ onMounted(() => loadChatUserGroups())
{{ row.name }}
-
- {{ row.is_auth ? '(自动授权)' : '(已授权 —)' }}
+ {{ row.is_auth ? '(自动授权)' : `(已授权 ${row.authorized_count ?? 0})` }}