feat(admin): /admin/users 新增按用户 API Key 所在分组过滤

- 支持专用/公开/订阅/已禁用四类分组:按 api_keys.group_id 精确匹配,
  排除软删除 key(EXISTS + DeletedAtIsNil);已禁用分组单独成区,
  覆盖 key 仍挂在禁用分组上的用户
- 后端:UserListFilters.APIKeyGroupID;handler 解析 api_key_group_id;
  repo HasAPIKeysWith 谓词;GetAllGroupsIncludingInactive 新接口
  (/admin/groups/all?include_inactive=true)
- 前端:下拉按类型分区单选;UsersView 独立 allGroupsForApiKeyFilter
  loader;分区标题用负数哨兵值修复 Vue :key 重复问题
- 测试:repo 集成 5/5(含软删除排除、多 key 去重、叠加 status 过滤)、
  handler 单测 5/5、前端 vitest 6/6

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DaydreamCoding
2026-06-08 22:54:01 +08:00
committed by QTom
co-authored by Claude Opus 4.8
parent be01744565
commit 329414ea4f
15 changed files with 529 additions and 12 deletions
+12
View File
@@ -57,6 +57,17 @@ export async function getAll(platform?: GroupPlatform): Promise<AdminGroup[]> {
return data
}
/**
* Get ALL groups including disabled ones — used by the API Key group filter so
* that admins can filter users whose keys are still bound to a now-disabled group.
*/
export async function getAllIncludingInactive(): Promise<AdminGroup[]> {
const { data } = await apiClient.get<AdminGroup[]>('/admin/groups/all', {
params: { include_inactive: true }
})
return data
}
/**
* Get active groups by platform
* @param platform - Platform to filter by
@@ -322,6 +333,7 @@ export const groupsAPI = {
list,
getAll,
getByPlatform,
getAllIncludingInactive,
getById,
getModelsListCandidates,
create,
+2
View File
@@ -60,6 +60,7 @@ export async function list(
role?: 'admin' | 'user'
search?: string
group_name?: string // fuzzy filter by allowed group name
api_key_group_id?: number // filter users by the group their API keys are bound to
attributes?: Record<number, string> // attributeId -> value
include_subscriptions?: boolean
sort_by?: string
@@ -77,6 +78,7 @@ export async function list(
role: filters?.role,
search: filters?.search,
group_name: filters?.group_name,
api_key_group_id: filters?.api_key_group_id,
include_subscriptions: filters?.include_subscriptions,
sort_by: filters?.sort_by,
sort_order: filters?.sort_order