feat: droid平台账户数据统计及调度能力

This commit is contained in:
shaw
2025-10-10 15:13:45 +08:00
parent 2fc84a6aca
commit 42db271848
21 changed files with 1424 additions and 212 deletions

View File

@@ -311,6 +311,10 @@
<input v-model="form.permissions" class="mr-2" type="radio" value="openai" />
<span class="text-sm text-gray-700"> OpenAI</span>
</label>
<label class="flex cursor-pointer items-center">
<input v-model="form.permissions" class="mr-2" type="radio" value="droid" />
<span class="text-sm text-gray-700"> Droid</span>
</label>
</div>
</div>
@@ -345,7 +349,7 @@
<select
v-model="form.claudeAccountId"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
:disabled="form.permissions === 'gemini' || form.permissions === 'openai'"
:disabled="form.permissions && !['all', 'claude'].includes(form.permissions)"
>
<option value="">不修改</option>
<option value="SHARED_POOL">使用共享账号池</option>
@@ -380,7 +384,7 @@
<select
v-model="form.geminiAccountId"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
:disabled="form.permissions === 'claude' || form.permissions === 'openai'"
:disabled="form.permissions && !['all', 'gemini'].includes(form.permissions)"
>
<option value="">不修改</option>
<option value="SHARED_POOL">使用共享账号池</option>
@@ -411,7 +415,7 @@
<select
v-model="form.openaiAccountId"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
:disabled="form.permissions === 'claude' || form.permissions === 'gemini'"
:disabled="form.permissions && !['all', 'openai'].includes(form.permissions)"
>
<option value="">不修改</option>
<option value="SHARED_POOL">使用共享账号池</option>
@@ -442,7 +446,7 @@
<select
v-model="form.bedrockAccountId"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
:disabled="form.permissions === 'gemini' || form.permissions === 'openai'"
:disabled="form.permissions && !['all', 'openai'].includes(form.permissions)"
>
<option value="">不修改</option>
<option value="SHARED_POOL">使用共享账号池</option>
@@ -457,6 +461,37 @@
</optgroup>
</select>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
>Droid 专属账号</label
>
<select
v-model="form.droidAccountId"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
:disabled="form.permissions && !['all', 'droid'].includes(form.permissions)"
>
<option value="">不修改</option>
<option value="SHARED_POOL">使用共享账号池</option>
<optgroup v-if="localAccounts.droidGroups.length > 0" label="账号分组">
<option
v-for="group in localAccounts.droidGroups"
:key="group.id"
:value="`group:${group.id}`"
>
分组 - {{ group.name }}
</option>
</optgroup>
<optgroup v-if="localAccounts.droid.length > 0" label="专属账号">
<option
v-for="account in localAccounts.droid"
:key="account.id"
:value="account.id"
>
{{ account.name }}
</option>
</optgroup>
</select>
</div>
</div>
</div>
@@ -497,7 +532,17 @@ const props = defineProps({
},
accounts: {
type: Object,
default: () => ({ claude: [], gemini: [], openai: [], bedrock: [] })
default: () => ({
claude: [],
gemini: [],
openai: [],
bedrock: [],
droid: [],
claudeGroups: [],
geminiGroups: [],
openaiGroups: [],
droidGroups: []
})
}
})
@@ -511,9 +556,11 @@ const localAccounts = ref({
gemini: [],
openai: [],
bedrock: [],
droid: [],
claudeGroups: [],
geminiGroups: [],
openaiGroups: []
openaiGroups: [],
droidGroups: []
})
// 标签相关
@@ -542,6 +589,7 @@ const form = reactive({
geminiAccountId: '',
openaiAccountId: '',
bedrockAccountId: '',
droidAccountId: '',
tags: [],
isActive: null // null表示不修改
})
@@ -571,15 +619,23 @@ const removeTag = (index) => {
const refreshAccounts = async () => {
accountsLoading.value = true
try {
const [claudeData, claudeConsoleData, geminiData, openaiData, bedrockData, groupsData] =
await Promise.all([
apiClient.get('/admin/claude-accounts'),
apiClient.get('/admin/claude-console-accounts'),
apiClient.get('/admin/gemini-accounts'),
apiClient.get('/admin/openai-accounts'),
apiClient.get('/admin/bedrock-accounts'),
apiClient.get('/admin/account-groups')
])
const [
claudeData,
claudeConsoleData,
geminiData,
openaiData,
bedrockData,
droidData,
groupsData
] = await Promise.all([
apiClient.get('/admin/claude-accounts'),
apiClient.get('/admin/claude-console-accounts'),
apiClient.get('/admin/gemini-accounts'),
apiClient.get('/admin/openai-accounts'),
apiClient.get('/admin/bedrock-accounts'),
apiClient.get('/admin/droid-accounts'),
apiClient.get('/admin/account-groups')
])
// 合并Claude OAuth账户和Claude Console账户
const claudeAccounts = []
@@ -627,12 +683,21 @@ const refreshAccounts = async () => {
}))
}
if (droidData.success) {
localAccounts.value.droid = (droidData.data || []).map((account) => ({
...account,
platform: 'droid',
isDedicated: account.accountType === 'dedicated'
}))
}
// 处理分组数据
if (groupsData.success) {
const allGroups = groupsData.data || []
localAccounts.value.claudeGroups = allGroups.filter((g) => g.platform === 'claude')
localAccounts.value.geminiGroups = allGroups.filter((g) => g.platform === 'gemini')
localAccounts.value.openaiGroups = allGroups.filter((g) => g.platform === 'openai')
localAccounts.value.droidGroups = allGroups.filter((g) => g.platform === 'droid')
}
showToast('账号列表已刷新', 'success')
@@ -720,6 +785,14 @@ const batchUpdateApiKeys = async () => {
}
}
if (form.droidAccountId !== '') {
if (form.droidAccountId === 'SHARED_POOL') {
updates.droidAccountId = null
} else {
updates.droidAccountId = form.droidAccountId
}
}
// 激活状态
if (form.isActive !== null) {
updates.isActive = form.isActive
@@ -774,9 +847,11 @@ onMounted(async () => {
gemini: props.accounts.gemini || [],
openai: props.accounts.openai || [],
bedrock: props.accounts.bedrock || [],
droid: props.accounts.droid || [],
claudeGroups: props.accounts.claudeGroups || [],
geminiGroups: props.accounts.geminiGroups || [],
openaiGroups: props.accounts.openaiGroups || []
openaiGroups: props.accounts.openaiGroups || [],
droidGroups: props.accounts.droidGroups || []
}
}
})

View File

@@ -616,6 +616,15 @@
/>
<span class="text-sm text-gray-700 dark:text-gray-300"> OpenAI</span>
</label>
<label class="flex cursor-pointer items-center">
<input
v-model="form.permissions"
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
type="radio"
value="droid"
/>
<span class="text-sm text-gray-700 dark:text-gray-300"> Droid</span>
</label>
</div>
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
控制此 API Key 可以访问哪些服务
@@ -653,7 +662,7 @@
v-model="form.claudeAccountId"
:accounts="localAccounts.claude"
default-option-text="使用共享账号池"
:disabled="form.permissions === 'gemini' || form.permissions === 'openai'"
:disabled="form.permissions !== 'all' && form.permissions !== 'claude'"
:groups="localAccounts.claudeGroups"
placeholder="请选择Claude账号"
platform="claude"
@@ -667,7 +676,7 @@
v-model="form.geminiAccountId"
:accounts="localAccounts.gemini"
default-option-text="使用共享账号池"
:disabled="form.permissions === 'claude' || form.permissions === 'openai'"
:disabled="form.permissions !== 'all' && form.permissions !== 'gemini'"
:groups="localAccounts.geminiGroups"
placeholder="请选择Gemini账号"
platform="gemini"
@@ -681,7 +690,7 @@
v-model="form.openaiAccountId"
:accounts="localAccounts.openai"
default-option-text="使用共享账号池"
:disabled="form.permissions === 'claude' || form.permissions === 'gemini'"
:disabled="form.permissions !== 'all' && form.permissions !== 'openai'"
:groups="localAccounts.openaiGroups"
placeholder="请选择OpenAI账号"
platform="openai"
@@ -695,12 +704,26 @@
v-model="form.bedrockAccountId"
:accounts="localAccounts.bedrock"
default-option-text="使用共享账号池"
:disabled="form.permissions === 'gemini' || form.permissions === 'openai'"
:disabled="form.permissions !== 'all' && form.permissions !== 'openai'"
:groups="[]"
placeholder="请选择Bedrock账号"
platform="bedrock"
/>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
>Droid 专属账号</label
>
<AccountSelector
v-model="form.droidAccountId"
:accounts="localAccounts.droid"
default-option-text="使用共享账号池"
:disabled="form.permissions !== 'all' && form.permissions !== 'droid'"
:groups="localAccounts.droidGroups"
placeholder="请选择Droid账号"
platform="droid"
/>
</div>
</div>
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
选择专属账号后此API Key将只使用该账号不选择则使用共享账号池
@@ -875,7 +898,17 @@ import AccountSelector from '@/components/common/AccountSelector.vue'
const props = defineProps({
accounts: {
type: Object,
default: () => ({ claude: [], gemini: [] })
default: () => ({
claude: [],
gemini: [],
openai: [],
bedrock: [],
droid: [],
claudeGroups: [],
geminiGroups: [],
openaiGroups: [],
droidGroups: []
})
}
})
@@ -889,10 +922,12 @@ const localAccounts = ref({
claude: [],
gemini: [],
openai: [],
bedrock: [], // 添加 Bedrock 账号列表
bedrock: [],
droid: [],
claudeGroups: [],
geminiGroups: [],
openaiGroups: []
openaiGroups: [],
droidGroups: []
})
// 表单验证状态
@@ -935,7 +970,8 @@ const form = reactive({
claudeAccountId: '',
geminiAccountId: '',
openaiAccountId: '',
bedrockAccountId: '', // 添加 Bedrock 账号ID
bedrockAccountId: '',
droidAccountId: '',
enableModelRestriction: false,
restrictedModels: [],
modelInput: '',
@@ -973,10 +1009,15 @@ onMounted(async () => {
claude: props.accounts.claude || [],
gemini: props.accounts.gemini || [],
openai: openaiAccounts,
bedrock: props.accounts.bedrock || [], // 添加 Bedrock 账号
bedrock: props.accounts.bedrock || [],
droid: (props.accounts.droid || []).map((account) => ({
...account,
platform: 'droid'
})),
claudeGroups: props.accounts.claudeGroups || [],
geminiGroups: props.accounts.geminiGroups || [],
openaiGroups: props.accounts.openaiGroups || []
openaiGroups: props.accounts.openaiGroups || [],
droidGroups: props.accounts.droidGroups || []
}
}
@@ -995,6 +1036,7 @@ const refreshAccounts = async () => {
openaiData,
openaiResponsesData,
bedrockData,
droidData,
groupsData
] = await Promise.all([
apiClient.get('/admin/claude-accounts'),
@@ -1002,7 +1044,8 @@ const refreshAccounts = async () => {
apiClient.get('/admin/gemini-accounts'),
apiClient.get('/admin/openai-accounts'),
apiClient.get('/admin/openai-responses-accounts'), // 获取 OpenAI-Responses 账号
apiClient.get('/admin/bedrock-accounts'), // 添加 Bedrock 账号获取
apiClient.get('/admin/bedrock-accounts'),
apiClient.get('/admin/droid-accounts'),
apiClient.get('/admin/account-groups')
])
@@ -1070,12 +1113,21 @@ const refreshAccounts = async () => {
}))
}
if (droidData.success) {
localAccounts.value.droid = (droidData.data || []).map((account) => ({
...account,
platform: 'droid',
isDedicated: account.accountType === 'dedicated'
}))
}
// 处理分组数据
if (groupsData.success) {
const allGroups = groupsData.data || []
localAccounts.value.claudeGroups = allGroups.filter((g) => g.platform === 'claude')
localAccounts.value.geminiGroups = allGroups.filter((g) => g.platform === 'gemini')
localAccounts.value.openaiGroups = allGroups.filter((g) => g.platform === 'openai')
localAccounts.value.droidGroups = allGroups.filter((g) => g.platform === 'droid')
}
showToast('账号列表已刷新', 'success')
@@ -1346,6 +1398,9 @@ const createApiKey = async () => {
if (form.bedrockAccountId) {
baseData.bedrockAccountId = form.bedrockAccountId
}
if (form.droidAccountId) {
baseData.droidAccountId = form.droidAccountId
}
if (form.createType === 'single') {
// 单个创建

View File

@@ -449,6 +449,15 @@
/>
<span class="text-sm text-gray-700 dark:text-gray-300"> OpenAI</span>
</label>
<label class="flex cursor-pointer items-center">
<input
v-model="form.permissions"
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
type="radio"
value="droid"
/>
<span class="text-sm text-gray-700 dark:text-gray-300"> Droid</span>
</label>
</div>
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
控制此 API Key 可以访问哪些服务
@@ -486,7 +495,7 @@
v-model="form.claudeAccountId"
:accounts="localAccounts.claude"
default-option-text="使用共享账号池"
:disabled="form.permissions === 'gemini' || form.permissions === 'openai'"
:disabled="form.permissions !== 'all' && form.permissions !== 'claude'"
:groups="localAccounts.claudeGroups"
placeholder="请选择Claude账号"
platform="claude"
@@ -500,7 +509,7 @@
v-model="form.geminiAccountId"
:accounts="localAccounts.gemini"
default-option-text="使用共享账号池"
:disabled="form.permissions === 'claude' || form.permissions === 'openai'"
:disabled="form.permissions !== 'all' && form.permissions !== 'gemini'"
:groups="localAccounts.geminiGroups"
placeholder="请选择Gemini账号"
platform="gemini"
@@ -514,7 +523,7 @@
v-model="form.openaiAccountId"
:accounts="localAccounts.openai"
default-option-text="使用共享账号池"
:disabled="form.permissions === 'claude' || form.permissions === 'gemini'"
:disabled="form.permissions !== 'all' && form.permissions !== 'openai'"
:groups="localAccounts.openaiGroups"
placeholder="请选择OpenAI账号"
platform="openai"
@@ -528,12 +537,26 @@
v-model="form.bedrockAccountId"
:accounts="localAccounts.bedrock"
default-option-text="使用共享账号池"
:disabled="form.permissions === 'gemini' || form.permissions === 'openai'"
:disabled="form.permissions !== 'all' && form.permissions !== 'openai'"
:groups="[]"
placeholder="请选择Bedrock账号"
platform="bedrock"
/>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
>Droid 专属账号</label
>
<AccountSelector
v-model="form.droidAccountId"
:accounts="localAccounts.droid"
default-option-text="使用共享账号池"
:disabled="form.permissions !== 'all' && form.permissions !== 'droid'"
:groups="localAccounts.droidGroups"
placeholder="请选择Droid账号"
platform="droid"
/>
</div>
</div>
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
修改绑定账号将影响此API Key的请求路由
@@ -717,7 +740,18 @@ const props = defineProps({
},
accounts: {
type: Object,
default: () => ({ claude: [], gemini: [] })
default: () => ({
claude: [],
gemini: [],
openai: [],
bedrock: [],
droid: [],
claudeGroups: [],
geminiGroups: [],
openaiGroups: [],
droidGroups: [],
openaiResponses: []
})
}
})
@@ -732,10 +766,12 @@ const localAccounts = ref({
claude: [],
gemini: [],
openai: [],
bedrock: [], // 添加 Bedrock 账号列表
bedrock: [],
droid: [],
claudeGroups: [],
geminiGroups: [],
openaiGroups: []
openaiGroups: [],
droidGroups: []
})
// 支持的客户端列表
@@ -768,7 +804,8 @@ const form = reactive({
claudeAccountId: '',
geminiAccountId: '',
openaiAccountId: '',
bedrockAccountId: '', // 添加 Bedrock 账号ID
bedrockAccountId: '',
droidAccountId: '',
enableModelRestriction: false,
restrictedModels: [],
modelInput: '',
@@ -930,6 +967,12 @@ const updateApiKey = async () => {
data.bedrockAccountId = null
}
if (form.droidAccountId) {
data.droidAccountId = form.droidAccountId
} else {
data.droidAccountId = null
}
// 模型限制 - 始终提交这些字段
data.enableModelRestriction = form.enableModelRestriction
data.restrictedModels = form.restrictedModels
@@ -972,14 +1015,16 @@ const refreshAccounts = async () => {
openaiData,
openaiResponsesData,
bedrockData,
droidData,
groupsData
] = await Promise.all([
apiClient.get('/admin/claude-accounts'),
apiClient.get('/admin/claude-console-accounts'),
apiClient.get('/admin/gemini-accounts'),
apiClient.get('/admin/openai-accounts'),
apiClient.get('/admin/openai-responses-accounts'), // 获取 OpenAI-Responses 账号
apiClient.get('/admin/bedrock-accounts'), // 添加 Bedrock 账号获取
apiClient.get('/admin/openai-responses-accounts'),
apiClient.get('/admin/bedrock-accounts'),
apiClient.get('/admin/droid-accounts'),
apiClient.get('/admin/account-groups')
])
@@ -1047,12 +1092,21 @@ const refreshAccounts = async () => {
}))
}
if (droidData.success) {
localAccounts.value.droid = (droidData.data || []).map((account) => ({
...account,
platform: 'droid',
isDedicated: account.accountType === 'dedicated'
}))
}
// 处理分组数据
if (groupsData.success) {
const allGroups = groupsData.data || []
localAccounts.value.claudeGroups = allGroups.filter((g) => g.platform === 'claude')
localAccounts.value.geminiGroups = allGroups.filter((g) => g.platform === 'gemini')
localAccounts.value.openaiGroups = allGroups.filter((g) => g.platform === 'openai')
localAccounts.value.droidGroups = allGroups.filter((g) => g.platform === 'droid')
}
showToast('账号列表已刷新', 'success')
@@ -1128,10 +1182,15 @@ onMounted(async () => {
claude: props.accounts.claude || [],
gemini: props.accounts.gemini || [],
openai: openaiAccounts,
bedrock: props.accounts.bedrock || [], // 添加 Bedrock 账号
bedrock: props.accounts.bedrock || [],
droid: (props.accounts.droid || []).map((account) => ({
...account,
platform: 'droid'
})),
claudeGroups: props.accounts.claudeGroups || [],
geminiGroups: props.accounts.geminiGroups || [],
openaiGroups: props.accounts.openaiGroups || []
openaiGroups: props.accounts.openaiGroups || [],
droidGroups: props.accounts.droidGroups || []
}
}
@@ -1168,7 +1227,8 @@ onMounted(async () => {
// 处理 OpenAI 账号 - 直接使用后端传来的值(已包含 responses: 前缀)
form.openaiAccountId = props.apiKey.openaiAccountId || ''
form.bedrockAccountId = props.apiKey.bedrockAccountId || '' // 添加 Bedrock 账号ID初始化
form.bedrockAccountId = props.apiKey.bedrockAccountId || ''
form.droidAccountId = props.apiKey.droidAccountId || ''
form.restrictedModels = props.apiKey.restrictedModels || []
form.allowedClients = props.apiKey.allowedClients || []
form.tags = props.apiKey.tags || []