mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
feat: 添加两级平台筛选功能(支持平台分组)
- 添加 platformHierarchy 定义平台层级结构(Claude全部、OpenAI全部、Gemini全部、Droid) - 添加 platformGroupMap 映射平台组到具体平台 - 添加 platformRequestHandlers 动态处理平台请求 - 将 platformOptions 从 ref 改为 computed 支持缩进显示 - 优化 loadAccounts 使用动态平台加载替代大型 switch 语句 - 新增 getPlatformsForFilter 辅助函数 功能说明: - 支持选择"Claude(全部)"同时筛选 claude + claude-console + bedrock + ccr - 支持选择"OpenAI(全部)"同时筛选 openai + openai-responses + azure_openai - 支持选择"Gemini(全部)"同时筛选 gemini + gemini-api - 保持向后兼容,仍支持单独选择具体平台
This commit is contained in:
@@ -2124,19 +2124,91 @@ const sortOptions = ref([
|
|||||||
{ value: 'rateLimitTime', label: '按限流时间排序', icon: 'fa-hourglass' }
|
{ value: 'rateLimitTime', label: '按限流时间排序', icon: 'fa-hourglass' }
|
||||||
])
|
])
|
||||||
|
|
||||||
const platformOptions = ref([
|
// 平台层级结构定义
|
||||||
{ value: 'all', label: '所有平台', icon: 'fa-globe' },
|
const platformHierarchy = [
|
||||||
{ value: 'claude', label: 'Claude', icon: 'fa-brain' },
|
{
|
||||||
{ value: 'claude-console', label: 'Claude Console', icon: 'fa-terminal' },
|
value: 'group-claude',
|
||||||
{ value: 'gemini', label: 'Gemini', icon: 'fab fa-google' },
|
label: 'Claude(全部)',
|
||||||
{ value: 'gemini-api', label: 'Gemini API', icon: 'fa-key' },
|
icon: 'fa-brain',
|
||||||
{ value: 'openai', label: 'OpenAi', icon: 'fa-openai' },
|
children: [
|
||||||
{ value: 'azure_openai', label: 'Azure OpenAI', icon: 'fab fa-microsoft' },
|
{ value: 'claude', label: 'Claude 官方/OAuth', icon: 'fa-brain' },
|
||||||
{ value: 'bedrock', label: 'Bedrock', icon: 'fab fa-aws' },
|
{ value: 'claude-console', label: 'Claude Console', icon: 'fa-terminal' },
|
||||||
{ value: 'openai-responses', label: 'OpenAI-Responses', icon: 'fa-server' },
|
{ value: 'bedrock', label: 'Bedrock', icon: 'fab fa-aws' },
|
||||||
{ value: 'ccr', label: 'CCR', icon: 'fa-code-branch' },
|
{ value: 'ccr', label: 'CCR Relay', icon: 'fa-code-branch' }
|
||||||
{ value: 'droid', label: 'Droid', icon: 'fa-robot' }
|
]
|
||||||
])
|
},
|
||||||
|
{
|
||||||
|
value: 'group-openai',
|
||||||
|
label: 'Codex / OpenAI(全部)',
|
||||||
|
icon: 'fa-openai',
|
||||||
|
children: [
|
||||||
|
{ value: 'openai', label: 'OpenAI 官方', icon: 'fa-openai' },
|
||||||
|
{ value: 'openai-responses', label: 'OpenAI-Responses (Codex)', icon: 'fa-server' },
|
||||||
|
{ value: 'azure_openai', label: 'Azure OpenAI', icon: 'fab fa-microsoft' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'group-gemini',
|
||||||
|
label: 'Gemini(全部)',
|
||||||
|
icon: 'fab fa-google',
|
||||||
|
children: [
|
||||||
|
{ value: 'gemini', label: 'Gemini OAuth', icon: 'fab fa-google' },
|
||||||
|
{ value: 'gemini-api', label: 'Gemini API', icon: 'fa-key' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'group-droid',
|
||||||
|
label: 'Droid(全部)',
|
||||||
|
icon: 'fa-robot',
|
||||||
|
children: [{ value: 'droid', label: 'Droid', icon: 'fa-robot' }]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 平台分组映射
|
||||||
|
const platformGroupMap = {
|
||||||
|
'group-claude': ['claude', 'claude-console', 'bedrock', 'ccr'],
|
||||||
|
'group-openai': ['openai', 'openai-responses', 'azure_openai'],
|
||||||
|
'group-gemini': ['gemini', 'gemini-api'],
|
||||||
|
'group-droid': ['droid']
|
||||||
|
}
|
||||||
|
|
||||||
|
// 平台请求处理器
|
||||||
|
const platformRequestHandlers = {
|
||||||
|
claude: (params) => apiClient.get('/admin/claude-accounts', { params }),
|
||||||
|
'claude-console': (params) => apiClient.get('/admin/claude-console-accounts', { params }),
|
||||||
|
bedrock: (params) => apiClient.get('/admin/bedrock-accounts', { params }),
|
||||||
|
gemini: (params) => apiClient.get('/admin/gemini-accounts', { params }),
|
||||||
|
openai: (params) => apiClient.get('/admin/openai-accounts', { params }),
|
||||||
|
azure_openai: (params) => apiClient.get('/admin/azure-openai-accounts', { params }),
|
||||||
|
'openai-responses': (params) => apiClient.get('/admin/openai-responses-accounts', { params }),
|
||||||
|
ccr: (params) => apiClient.get('/admin/ccr-accounts', { params }),
|
||||||
|
droid: (params) => apiClient.get('/admin/droid-accounts', { params }),
|
||||||
|
'gemini-api': (params) => apiClient.get('/admin/gemini-api-accounts', { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
const allPlatformKeys = Object.keys(platformRequestHandlers)
|
||||||
|
|
||||||
|
// 根据过滤器获取需要加载的平台列表
|
||||||
|
const getPlatformsForFilter = (filter) => {
|
||||||
|
if (filter === 'all') return allPlatformKeys
|
||||||
|
if (platformGroupMap[filter]) return platformGroupMap[filter]
|
||||||
|
if (allPlatformKeys.includes(filter)) return [filter]
|
||||||
|
return allPlatformKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
// 平台选项(两级结构)
|
||||||
|
const platformOptions = computed(() => {
|
||||||
|
const options = [{ value: 'all', label: '所有平台', icon: 'fa-globe', indent: 0 }]
|
||||||
|
|
||||||
|
platformHierarchy.forEach((group) => {
|
||||||
|
options.push({ ...group, indent: 0, isGroup: true })
|
||||||
|
group.children?.forEach((child) => {
|
||||||
|
options.push({ ...child, indent: 1, parent: group.value })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return options
|
||||||
|
})
|
||||||
|
|
||||||
const statusOptions = ref([
|
const statusOptions = ref([
|
||||||
{ value: 'normal', label: '正常', icon: 'fa-check-circle' },
|
{ value: 'normal', label: '正常', icon: 'fa-check-circle' },
|
||||||
@@ -2696,190 +2768,14 @@ const loadAccounts = async (forceReload = false) => {
|
|||||||
try {
|
try {
|
||||||
// 构建查询参数(用于其他筛选情况)
|
// 构建查询参数(用于其他筛选情况)
|
||||||
const params = {}
|
const params = {}
|
||||||
if (platformFilter.value !== 'all') {
|
if (platformFilter.value !== 'all' && !platformGroupMap[platformFilter.value]) {
|
||||||
params.platform = platformFilter.value
|
params.platform = platformFilter.value
|
||||||
}
|
}
|
||||||
if (groupFilter.value !== 'all') {
|
if (groupFilter.value !== 'all') {
|
||||||
params.groupId = groupFilter.value
|
params.groupId = groupFilter.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据平台筛选决定需要请求哪些接口
|
const platformsToFetch = getPlatformsForFilter(platformFilter.value)
|
||||||
const requests = []
|
|
||||||
|
|
||||||
if (platformFilter.value === 'all') {
|
|
||||||
// 请求所有平台
|
|
||||||
requests.push(
|
|
||||||
apiClient.get('/admin/claude-accounts', { params }),
|
|
||||||
apiClient.get('/admin/claude-console-accounts', { params }),
|
|
||||||
apiClient.get('/admin/bedrock-accounts', { params }),
|
|
||||||
apiClient.get('/admin/gemini-accounts', { params }),
|
|
||||||
apiClient.get('/admin/openai-accounts', { params }),
|
|
||||||
apiClient.get('/admin/azure-openai-accounts', { params }),
|
|
||||||
apiClient.get('/admin/openai-responses-accounts', { params }),
|
|
||||||
apiClient.get('/admin/ccr-accounts', { params }),
|
|
||||||
apiClient.get('/admin/droid-accounts', { params }),
|
|
||||||
apiClient.get('/admin/gemini-api-accounts', { params })
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// 只请求指定平台,其他平台设为null占位
|
|
||||||
switch (platformFilter.value) {
|
|
||||||
case 'claude':
|
|
||||||
requests.push(
|
|
||||||
apiClient.get('/admin/claude-accounts', { params }),
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude-console 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // bedrock 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // gemini 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // azure-openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai-responses 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // ccr 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // droid 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }) // gemini-api 占位
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case 'claude-console':
|
|
||||||
requests.push(
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude 占位
|
|
||||||
apiClient.get('/admin/claude-console-accounts', { params }),
|
|
||||||
Promise.resolve({ success: true, data: [] }), // bedrock 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // gemini 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // azure-openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai-responses 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // ccr 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // droid 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }) // gemini-api 占位
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case 'bedrock':
|
|
||||||
requests.push(
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude-console 占位
|
|
||||||
apiClient.get('/admin/bedrock-accounts', { params }),
|
|
||||||
Promise.resolve({ success: true, data: [] }), // gemini 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // azure-openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai-responses 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // ccr 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // droid 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }) // gemini-api 占位
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case 'gemini':
|
|
||||||
requests.push(
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude-console 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // bedrock 占位
|
|
||||||
apiClient.get('/admin/gemini-accounts', { params }),
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // azure-openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai-responses 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // ccr 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // droid 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }) // gemini-api 占位
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case 'openai':
|
|
||||||
requests.push(
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude-console 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // bedrock 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // gemini 占位
|
|
||||||
apiClient.get('/admin/openai-accounts', { params }),
|
|
||||||
Promise.resolve({ success: true, data: [] }), // azure-openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai-responses 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // ccr 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // droid 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }) // gemini-api 占位
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case 'azure_openai':
|
|
||||||
requests.push(
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude-console 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // bedrock 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // gemini 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai 占位
|
|
||||||
apiClient.get('/admin/azure-openai-accounts', { params }),
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai-responses 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // ccr 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // droid 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }) // gemini-api 占位
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case 'openai-responses':
|
|
||||||
requests.push(
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude-console 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // bedrock 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // gemini 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // azure-openai 占位
|
|
||||||
apiClient.get('/admin/openai-responses-accounts', { params }),
|
|
||||||
Promise.resolve({ success: true, data: [] }), // ccr 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // droid 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }) // gemini-api 占位
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case 'ccr':
|
|
||||||
requests.push(
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude-console 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // bedrock 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // gemini 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // azure 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai-responses 占位
|
|
||||||
apiClient.get('/admin/ccr-accounts', { params }),
|
|
||||||
Promise.resolve({ success: true, data: [] }), // droid 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }) // gemini-api 占位
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case 'droid':
|
|
||||||
requests.push(
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude-console 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // bedrock 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // gemini 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // azure 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai-responses 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // ccr 占位
|
|
||||||
apiClient.get('/admin/droid-accounts', { params }),
|
|
||||||
Promise.resolve({ success: true, data: [] }) // gemini-api 占位
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case 'gemini-api':
|
|
||||||
requests.push(
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // claude-console 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // bedrock 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // gemini 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // azure-openai 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // openai-responses 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // ccr 占位
|
|
||||||
Promise.resolve({ success: true, data: [] }), // droid 占位
|
|
||||||
apiClient.get('/admin/gemini-api-accounts', { params })
|
|
||||||
)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
// 默认情况下返回空数组
|
|
||||||
requests.push(
|
|
||||||
Promise.resolve({ success: true, data: [] }),
|
|
||||||
Promise.resolve({ success: true, data: [] }),
|
|
||||||
Promise.resolve({ success: true, data: [] }),
|
|
||||||
Promise.resolve({ success: true, data: [] }),
|
|
||||||
Promise.resolve({ success: true, data: [] }),
|
|
||||||
Promise.resolve({ success: true, data: [] }),
|
|
||||||
Promise.resolve({ success: true, data: [] }),
|
|
||||||
Promise.resolve({ success: true, data: [] }),
|
|
||||||
Promise.resolve({ success: true, data: [] }),
|
|
||||||
Promise.resolve({ success: true, data: [] })
|
|
||||||
)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用缓存机制加载绑定计数和分组数据(不再加载完整的 API Keys 数据)
|
// 使用缓存机制加载绑定计数和分组数据(不再加载完整的 API Keys 数据)
|
||||||
await Promise.all([loadBindingCounts(forceReload), loadAccountGroups(forceReload)])
|
await Promise.all([loadBindingCounts(forceReload), loadAccountGroups(forceReload)])
|
||||||
@@ -2887,125 +2783,137 @@ const loadAccounts = async (forceReload = false) => {
|
|||||||
// 后端账户API已经包含分组信息,不需要单独加载分组成员关系
|
// 后端账户API已经包含分组信息,不需要单独加载分组成员关系
|
||||||
// await loadGroupMembers(forceReload)
|
// await loadGroupMembers(forceReload)
|
||||||
|
|
||||||
const [
|
const platformResults = await Promise.all(
|
||||||
claudeData,
|
platformsToFetch.map(async (platform) => {
|
||||||
claudeConsoleData,
|
const handler = platformRequestHandlers[platform]
|
||||||
bedrockData,
|
if (!handler) {
|
||||||
geminiData,
|
return { platform, success: true, data: [] }
|
||||||
openaiData,
|
}
|
||||||
azureOpenaiData,
|
|
||||||
openaiResponsesData,
|
|
||||||
ccrData,
|
|
||||||
droidData,
|
|
||||||
geminiApiData
|
|
||||||
] = await Promise.all(requests)
|
|
||||||
|
|
||||||
const allAccounts = []
|
try {
|
||||||
|
const res = await handler(params)
|
||||||
// 获取绑定计数数据
|
return { platform, success: res?.success, data: res?.data }
|
||||||
const counts = bindingCounts.value
|
} catch (error) {
|
||||||
|
console.debug(`Failed to load ${platform} accounts:`, error)
|
||||||
if (claudeData.success) {
|
return { platform, success: false, data: [] }
|
||||||
const claudeAccounts = (claudeData.data || []).map((acc) => {
|
|
||||||
// 从绑定计数缓存获取数量
|
|
||||||
const boundApiKeysCount = counts.claudeAccountId?.[acc.id] || 0
|
|
||||||
// 后端已经包含了groupInfos,直接使用
|
|
||||||
return { ...acc, platform: 'claude', boundApiKeysCount }
|
|
||||||
})
|
|
||||||
allAccounts.push(...claudeAccounts)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (claudeConsoleData.success) {
|
|
||||||
const claudeConsoleAccounts = (claudeConsoleData.data || []).map((acc) => {
|
|
||||||
// 从绑定计数缓存获取数量
|
|
||||||
const boundApiKeysCount = counts.claudeConsoleAccountId?.[acc.id] || 0
|
|
||||||
// 后端已经包含了groupInfos,直接使用
|
|
||||||
return { ...acc, platform: 'claude-console', boundApiKeysCount }
|
|
||||||
})
|
|
||||||
allAccounts.push(...claudeConsoleAccounts)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bedrockData.success) {
|
|
||||||
const bedrockAccounts = (bedrockData.data || []).map((acc) => {
|
|
||||||
// Bedrock账户暂时不支持直接绑定
|
|
||||||
// 后端已经包含了groupInfos,直接使用
|
|
||||||
return { ...acc, platform: 'bedrock', boundApiKeysCount: 0 }
|
|
||||||
})
|
|
||||||
allAccounts.push(...bedrockAccounts)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (geminiData.success) {
|
|
||||||
const geminiAccounts = (geminiData.data || []).map((acc) => {
|
|
||||||
// 从绑定计数缓存获取数量
|
|
||||||
const boundApiKeysCount = counts.geminiAccountId?.[acc.id] || 0
|
|
||||||
// 后端已经包含了groupInfos,直接使用
|
|
||||||
return { ...acc, platform: 'gemini', boundApiKeysCount }
|
|
||||||
})
|
|
||||||
allAccounts.push(...geminiAccounts)
|
|
||||||
}
|
|
||||||
if (openaiData.success) {
|
|
||||||
const openaiAccounts = (openaiData.data || []).map((acc) => {
|
|
||||||
// 从绑定计数缓存获取数量
|
|
||||||
const boundApiKeysCount = counts.openaiAccountId?.[acc.id] || 0
|
|
||||||
// 后端已经包含了groupInfos,直接使用
|
|
||||||
return { ...acc, platform: 'openai', boundApiKeysCount }
|
|
||||||
})
|
|
||||||
allAccounts.push(...openaiAccounts)
|
|
||||||
}
|
|
||||||
if (azureOpenaiData && azureOpenaiData.success) {
|
|
||||||
const azureOpenaiAccounts = (azureOpenaiData.data || []).map((acc) => {
|
|
||||||
// 从绑定计数缓存获取数量
|
|
||||||
const boundApiKeysCount = counts.azureOpenaiAccountId?.[acc.id] || 0
|
|
||||||
// 后端已经包含了groupInfos,直接使用
|
|
||||||
return { ...acc, platform: 'azure_openai', boundApiKeysCount }
|
|
||||||
})
|
|
||||||
allAccounts.push(...azureOpenaiAccounts)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (openaiResponsesData && openaiResponsesData.success) {
|
|
||||||
const openaiResponsesAccounts = (openaiResponsesData.data || []).map((acc) => {
|
|
||||||
// 从绑定计数缓存获取数量
|
|
||||||
// OpenAI-Responses账户使用 responses: 前缀
|
|
||||||
const boundApiKeysCount = counts.openaiAccountId?.[`responses:${acc.id}`] || 0
|
|
||||||
// 后端已经包含了groupInfos,直接使用
|
|
||||||
return { ...acc, platform: 'openai-responses', boundApiKeysCount }
|
|
||||||
})
|
|
||||||
allAccounts.push(...openaiResponsesAccounts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CCR 账户
|
|
||||||
if (ccrData && ccrData.success) {
|
|
||||||
const ccrAccounts = (ccrData.data || []).map((acc) => {
|
|
||||||
// CCR 不支持 API Key 绑定,固定为 0
|
|
||||||
return { ...acc, platform: 'ccr', boundApiKeysCount: 0 }
|
|
||||||
})
|
|
||||||
allAccounts.push(...ccrAccounts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Droid 账户
|
|
||||||
if (droidData && droidData.success) {
|
|
||||||
const droidAccounts = (droidData.data || []).map((acc) => {
|
|
||||||
// 从绑定计数缓存获取数量
|
|
||||||
const boundApiKeysCount = counts.droidAccountId?.[acc.id] || acc.boundApiKeysCount || 0
|
|
||||||
return {
|
|
||||||
...acc,
|
|
||||||
platform: 'droid',
|
|
||||||
boundApiKeysCount
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
allAccounts.push(...droidAccounts)
|
)
|
||||||
|
|
||||||
|
const allAccounts = []
|
||||||
|
const counts = bindingCounts.value || {}
|
||||||
|
let openaiResponsesRaw = []
|
||||||
|
|
||||||
|
const appendAccounts = (platform, data) => {
|
||||||
|
if (!data || data.length === 0) return
|
||||||
|
|
||||||
|
switch (platform) {
|
||||||
|
case 'claude': {
|
||||||
|
const items = data.map((acc) => {
|
||||||
|
const boundApiKeysCount = counts.claudeAccountId?.[acc.id] || 0
|
||||||
|
return { ...acc, platform: 'claude', boundApiKeysCount }
|
||||||
|
})
|
||||||
|
allAccounts.push(...items)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'claude-console': {
|
||||||
|
const items = data.map((acc) => {
|
||||||
|
const boundApiKeysCount = counts.claudeConsoleAccountId?.[acc.id] || 0
|
||||||
|
return { ...acc, platform: 'claude-console', boundApiKeysCount }
|
||||||
|
})
|
||||||
|
allAccounts.push(...items)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'bedrock': {
|
||||||
|
const items = data.map((acc) => ({ ...acc, platform: 'bedrock', boundApiKeysCount: 0 }))
|
||||||
|
allAccounts.push(...items)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'gemini': {
|
||||||
|
const items = data.map((acc) => {
|
||||||
|
const boundApiKeysCount = counts.geminiAccountId?.[acc.id] || 0
|
||||||
|
return { ...acc, platform: 'gemini', boundApiKeysCount }
|
||||||
|
})
|
||||||
|
allAccounts.push(...items)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'openai': {
|
||||||
|
const items = data.map((acc) => {
|
||||||
|
const boundApiKeysCount = counts.openaiAccountId?.[acc.id] || 0
|
||||||
|
return { ...acc, platform: 'openai', boundApiKeysCount }
|
||||||
|
})
|
||||||
|
allAccounts.push(...items)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'azure_openai': {
|
||||||
|
const items = data.map((acc) => {
|
||||||
|
const boundApiKeysCount = counts.azureOpenaiAccountId?.[acc.id] || 0
|
||||||
|
return { ...acc, platform: 'azure_openai', boundApiKeysCount }
|
||||||
|
})
|
||||||
|
allAccounts.push(...items)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'openai-responses': {
|
||||||
|
openaiResponsesRaw = data
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'ccr': {
|
||||||
|
const items = data.map((acc) => ({ ...acc, platform: 'ccr', boundApiKeysCount: 0 }))
|
||||||
|
allAccounts.push(...items)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'droid': {
|
||||||
|
const items = data.map((acc) => {
|
||||||
|
const boundApiKeysCount = counts.droidAccountId?.[acc.id] || acc.boundApiKeysCount || 0
|
||||||
|
return { ...acc, platform: 'droid', boundApiKeysCount }
|
||||||
|
})
|
||||||
|
allAccounts.push(...items)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'gemini-api': {
|
||||||
|
const items = data.map((acc) => {
|
||||||
|
const boundApiKeysCount = counts.geminiAccountId?.[`api:${acc.id}`] || 0
|
||||||
|
return { ...acc, platform: 'gemini-api', boundApiKeysCount }
|
||||||
|
})
|
||||||
|
allAccounts.push(...items)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gemini API 账户
|
platformResults.forEach(({ platform, success, data }) => {
|
||||||
if (geminiApiData && geminiApiData.success) {
|
if (success) {
|
||||||
const geminiApiAccounts = (geminiApiData.data || []).map((acc) => {
|
appendAccounts(platform, data || [])
|
||||||
// 从绑定计数缓存获取数量
|
}
|
||||||
// Gemini-API账户使用 api: 前缀
|
})
|
||||||
const boundApiKeysCount = counts.geminiAccountId?.[`api:${acc.id}`] || 0
|
|
||||||
// 后端已经包含了groupInfos,直接使用
|
if (openaiResponsesRaw.length > 0) {
|
||||||
return { ...acc, platform: 'gemini-api', boundApiKeysCount }
|
let autoRecoveryConfigMap = {}
|
||||||
|
try {
|
||||||
|
const configsRes = await apiClient.get(
|
||||||
|
'/admin/openai-responses-accounts/auto-recovery-configs'
|
||||||
|
)
|
||||||
|
if (configsRes.success && Array.isArray(configsRes.data)) {
|
||||||
|
autoRecoveryConfigMap = configsRes.data.reduce((map, config) => {
|
||||||
|
if (config?.accountId) {
|
||||||
|
map[config.accountId] = config
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.debug('Failed to load auto-recovery configs:', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const responsesAccounts = openaiResponsesRaw.map((acc) => {
|
||||||
|
const boundApiKeysCount = counts.openaiAccountId?.[`responses:${acc.id}`] || 0
|
||||||
|
const autoRecoveryConfig = autoRecoveryConfigMap[acc.id] || acc.autoRecoveryConfig || null
|
||||||
|
return { ...acc, platform: 'openai-responses', boundApiKeysCount, autoRecoveryConfig }
|
||||||
})
|
})
|
||||||
allAccounts.push(...geminiApiAccounts)
|
|
||||||
|
allAccounts.push(...responsesAccounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据分组筛选器过滤账户
|
// 根据分组筛选器过滤账户
|
||||||
|
|||||||
Reference in New Issue
Block a user