feat(pool): 号池额度主动探测、封禁自动清除、调度硬优先级与前端重构

- 新增 PoolQuotaProbeScheduler,按 probing_interval_minutes 主动探测静默 Key 额度
- pool_advanced 增加 probing_enabled / auto_remove_banned_keys 配置项
- error_handler 和 quota_service 支持封禁 Key 自动删除及缓存清理
- multi_score 策略从加权混合重构为硬优先级排序,引入 mutex_group 互斥组
- 指纹注入从 handler 层下移至 ClaudeCode envelope 层
- OAuth 批量导入支持 concurrency 并发参数
- 前端号池管理拆分高级设置/账号批量/代理设置为独立组件
- 号池总览接口精简,仅返回已启用调度的 Provider
This commit is contained in:
fawney19
2026-03-05 15:15:26 +08:00
parent fdb50a065b
commit b1be413dc0
29 changed files with 3098 additions and 852 deletions

View File

@@ -709,6 +709,28 @@ const poolAttemptCandidates = computed<CandidateRecord[]>(() => {
const attempts = audit.attempts
if (!Array.isArray(attempts) || attempts.length === 0) return []
const providerNameById = new Map<string, string>()
for (const candidate of rawTimeline.value) {
const providerId = String(candidate.provider_id || '').trim()
const providerName = String(candidate.provider_name || '').trim()
if (!providerId || !providerName) continue
if (!providerNameById.has(providerId)) {
providerNameById.set(providerId, providerName)
}
}
const providerTypeLikeNames = new Set<string>([
'codex',
'kiro',
'antigravity',
'claude_code',
'claude code',
'gemini_cli',
'gemini cli',
'oauth',
'api_key',
'api key',
])
const traceMap = new Map<string, CandidateRecord>()
for (const candidate of rawTimeline.value) {
traceMap.set(makeAttemptKey(candidate.candidate_index, candidate.retry_index), candidate)
@@ -757,6 +779,21 @@ const poolAttemptCandidates = computed<CandidateRecord[]>(() => {
pool_group_id: finalPoolGroupId,
}
}
const mergedProviderId = String(merged.provider_id || '').trim()
if (mergedProviderId) {
const inferredProviderName = providerNameById.get(mergedProviderId)
const currentProviderName = String(merged.provider_name || '').trim()
if (
inferredProviderName
&& (
!currentProviderName
|| providerTypeLikeNames.has(currentProviderName.toLowerCase())
)
) {
merged.provider_name = inferredProviderName
}
}
return merged
})
.filter((item): item is CandidateRecord => item !== null)
@@ -807,14 +844,23 @@ const normalizeProviderName = (value: string): string => {
return text.replace(/反代$/u, '').trim() || text
}
const getProviderDisplayName = (attempt: CandidateRecord | null | undefined): string => {
const getProviderDisplayName = (
attempt: CandidateRecord | null | undefined,
options: { allowAuthTypeFallback?: boolean } = {},
): string => {
const allowAuthTypeFallback = options.allowAuthTypeFallback ?? true
if (!attempt) return '未知'
const authType = String(attempt.key_auth_type || '').trim().toLowerCase()
if (authType && AUTH_TYPE_PROVIDER_LABEL_MAP[authType]) {
return AUTH_TYPE_PROVIDER_LABEL_MAP[authType]
}
// 优先使用提供商名称(管理后台设置的名称)
const providerName = String(attempt.provider_name || '').trim()
return providerName ? normalizeProviderName(providerName) : '未知'
if (providerName) return normalizeProviderName(providerName)
if (allowAuthTypeFallback) {
// 回退:根据 auth_type 推断显示名称
const authType = String(attempt.key_auth_type || '').trim().toLowerCase()
if (authType && AUTH_TYPE_PROVIDER_LABEL_MAP[authType]) {
return AUTH_TYPE_PROVIDER_LABEL_MAP[authType]
}
}
return '未知'
}
const normalizeProviderIdentity = (value: unknown): string => {
@@ -898,7 +944,7 @@ const groupedTimeline = computed<NodeGroup[]>(() => {
poolGroups.push({
id: `pool:${groupId}`,
providerName: getProviderDisplayName(poolPrimary),
providerName: getProviderDisplayName(poolPrimary, { allowAuthTypeFallback: false }),
primary: poolPrimary,
primaryStatus: poolPrimaryStatus,
allAttempts: attempts,