fix(pool): 对齐账号批量配置语义

This commit is contained in:
MMEXA
2026-07-14 08:07:28 +08:00
parent 0acd1de29c
commit 01e104d86a
8 changed files with 227 additions and 118 deletions
@@ -23,12 +23,14 @@ function state(overrides: Partial<PoolKeyBatchEditState> = {}): PoolKeyBatchEdit
maxProbeIntervalMinutes: '32',
applyNote: false,
note: '',
applyModels: false,
modelMode: '',
unrestrictedModels: true,
selectedModels: [],
applyAutoFetchModels: false,
autoFetchModels: false,
includePatterns: '',
excludePatterns: '',
applyAllowedModels: false,
unrestrictedModels: true,
selectedModels: [],
lockSelectedModels: true,
...overrides,
}
}
@@ -49,40 +51,62 @@ describe('buildPoolKeyBatchUpdatePatch', () => {
})
})
it('builds a manual model policy and preserves explicit restrictions while disabling discovery', () => {
it('builds an explicit model access range without changing automatic discovery', () => {
const result = buildPoolKeyBatchUpdatePatch(state({
applyModels: true,
modelMode: 'manual',
applyAllowedModels: true,
unrestrictedModels: false,
selectedModels: ['gpt-5.6-sol', 'gpt-5.6-sol', 'gpt-5.6-luna'],
lockSelectedModels: false,
}))
expect(result.patch).toEqual({
auto_fetch_models: false,
allowed_models: ['gpt-5.6-sol', 'gpt-5.6-luna'],
locked_models: [],
model_include_patterns: [],
model_exclude_patterns: [],
})
})
it('builds automatic discovery filters and locked models', () => {
it('builds automatic discovery filters independently from the model access range', () => {
const result = buildPoolKeyBatchUpdatePatch(state({
applyModels: true,
modelMode: 'automatic',
selectedModels: ['gpt-5.6-sol'],
applyAutoFetchModels: true,
autoFetchModels: true,
includePatterns: 'gpt-*,\nclaude-*',
excludePatterns: '*-preview, *-beta',
}))
expect(result.patch).toEqual({
auto_fetch_models: true,
locked_models: ['gpt-5.6-sol'],
model_include_patterns: ['gpt-*', 'claude-*'],
model_exclude_patterns: ['*-preview', '*-beta'],
})
})
it('disables automatic discovery without rewriting model filters or access limits', () => {
const result = buildPoolKeyBatchUpdatePatch(state({
applyAutoFetchModels: true,
autoFetchModels: false,
includePatterns: 'gpt-*',
excludePatterns: '*-preview',
}))
expect(result.patch).toEqual({
auto_fetch_models: false,
})
})
it('locks selected models only when the operator enables locking', () => {
const result = buildPoolKeyBatchUpdatePatch(state({
applyAllowedModels: true,
unrestrictedModels: false,
selectedModels: ['gpt-5.6-sol'],
lockSelectedModels: true,
}))
expect(result.patch).toEqual({
allowed_models: ['gpt-5.6-sol'],
locked_models: ['gpt-5.6-sol'],
})
})
it('rejects empty fields and invalid ranges before the request is sent', () => {
expect(buildPoolKeyBatchUpdatePatch(state()).error).toBe('请至少启用一个批量编辑字段')
expect(buildPoolKeyBatchUpdatePatch(state({
@@ -93,10 +117,9 @@ describe('buildPoolKeyBatchUpdatePatch', () => {
cacheTtlMinutes: '61',
})).error).toBe('缓存 TTL 必须是 0-60 的整数')
expect(buildPoolKeyBatchUpdatePatch(state({
applyModels: true,
modelMode: 'manual',
applyAllowedModels: true,
unrestrictedModels: false,
})).error).toBe('请至少选择一个允许的模型')
})).error).toBe('请至少选择一个可用模型')
})
})
@@ -1,7 +1,5 @@
import type { PoolKeyBatchUpdatePatch } from '@/api/endpoints/pool'
export type PoolKeyBatchModelMode = '' | 'manual' | 'automatic'
export interface PoolKeyBatchEditState {
applyApiFormats: boolean
apiFormats: string[]
@@ -19,12 +17,14 @@ export interface PoolKeyBatchEditState {
maxProbeIntervalMinutes: string
applyNote: boolean
note: string
applyModels: boolean
modelMode: PoolKeyBatchModelMode
unrestrictedModels: boolean
selectedModels: string[]
applyAutoFetchModels: boolean
autoFetchModels: boolean
includePatterns: string
excludePatterns: string
applyAllowedModels: boolean
unrestrictedModels: boolean
selectedModels: string[]
lockSelectedModels: boolean
}
export interface PoolKeyBatchPatchBuildResult {
@@ -120,27 +120,25 @@ export function buildPoolKeyBatchUpdatePatch(
fieldLabels.push('备注')
}
if (state.applyModels) {
if (!state.modelMode) {
return { patch: null, fieldLabels, error: '请选择模型权限管理方式' }
}
const selectedModels = uniqueTrimmed(state.selectedModels)
if (state.modelMode === 'manual') {
if (!state.unrestrictedModels && selectedModels.length === 0) {
return { patch: null, fieldLabels, error: '请至少选择一个允许的模型' }
}
patch.auto_fetch_models = false
patch.allowed_models = state.unrestrictedModels ? null : selectedModels
patch.locked_models = []
patch.model_include_patterns = []
patch.model_exclude_patterns = []
} else {
patch.auto_fetch_models = true
patch.locked_models = selectedModels
if (state.applyAutoFetchModels) {
patch.auto_fetch_models = state.autoFetchModels
if (state.autoFetchModels) {
patch.model_include_patterns = parsePoolKeyModelPatterns(state.includePatterns)
patch.model_exclude_patterns = parsePoolKeyModelPatterns(state.excludePatterns)
}
fieldLabels.push('模型权限')
fieldLabels.push('自动获取上游可用模型')
}
if (state.applyAllowedModels) {
const selectedModels = uniqueTrimmed(state.selectedModels)
if (!state.unrestrictedModels && selectedModels.length === 0) {
return { patch: null, fieldLabels, error: '请至少选择一个可用模型' }
}
patch.allowed_models = state.unrestrictedModels ? null : selectedModels
patch.locked_models = state.unrestrictedModels || !state.lockSelectedModels
? []
: selectedModels
fieldLabels.push('可用模型范围')
}
if (fieldLabels.length === 0) {