diff --git a/src/routes/admin/usageStats.js b/src/routes/admin/usageStats.js index bf35f8a0..aa74c8a2 100644 --- a/src/routes/admin/usageStats.js +++ b/src/routes/admin/usageStats.js @@ -8,6 +8,7 @@ const geminiApiAccountService = require('../../services/geminiApiAccountService' const openaiAccountService = require('../../services/openaiAccountService') const openaiResponsesAccountService = require('../../services/openaiResponsesAccountService') const droidAccountService = require('../../services/droidAccountService') +const bedrockAccountService = require('../../services/bedrockAccountService') const redis = require('../../models/redis') const { authenticateAdmin } = require('../../middleware/auth') const logger = require('../../utils/logger') @@ -25,6 +26,7 @@ const accountTypeNames = { gemini: 'Gemini', 'gemini-api': 'Gemini API', droid: 'Droid', + bedrock: 'AWS Bedrock', unknown: '未知渠道' } @@ -37,7 +39,8 @@ const resolveAccountByPlatform = async (accountId, platform) => { openai: openaiAccountService, 'openai-responses': openaiResponsesAccountService, droid: droidAccountService, - ccr: ccrAccountService + ccr: ccrAccountService, + bedrock: bedrockAccountService } if (platform && serviceMap[platform]) { @@ -161,7 +164,8 @@ router.get('/accounts/:accountId/usage-history', authenticateAdmin, async (req, 'openai-responses', 'gemini', 'gemini-api', - 'droid' + 'droid', + 'bedrock' ] if (!allowedPlatforms.includes(platform)) { return res.status(400).json({ @@ -174,7 +178,8 @@ router.get('/accounts/:accountId/usage-history', authenticateAdmin, async (req, openai: 'openai', 'openai-responses': 'openai-responses', 'gemini-api': 'gemini-api', - droid: 'droid' + droid: 'droid', + bedrock: 'bedrock' } const fallbackModelMap = { @@ -184,7 +189,8 @@ router.get('/accounts/:accountId/usage-history', authenticateAdmin, async (req, 'openai-responses': 'gpt-4o-mini-2024-07-18', gemini: 'gemini-1.5-flash', 'gemini-api': 'gemini-2.0-flash', - droid: 'unknown' + droid: 'unknown', + bedrock: 'us.anthropic.claude-3-5-sonnet-20241022-v2:0' } // 获取账户信息以获取创建时间 @@ -215,6 +221,11 @@ router.get('/accounts/:accountId/usage-history', authenticateAdmin, async (req, case 'droid': accountData = await droidAccountService.getAccount(accountId) break + case 'bedrock': { + const result = await bedrockAccountService.getAccount(accountId) + accountData = result?.success ? result.data : null + break + } } if (accountData && accountData.createdAt) { @@ -882,7 +893,7 @@ router.get('/account-usage-trend', authenticateAdmin, async (req, res) => { try { const { granularity = 'day', group = 'claude', days = 7, startDate, endDate } = req.query - const allowedGroups = ['claude', 'openai', 'gemini', 'droid'] + const allowedGroups = ['claude', 'openai', 'gemini', 'droid', 'bedrock'] if (!allowedGroups.includes(group)) { return res.status(400).json({ success: false, @@ -894,7 +905,8 @@ router.get('/account-usage-trend', authenticateAdmin, async (req, res) => { claude: 'Claude账户', openai: 'OpenAI账户', gemini: 'Gemini账户', - droid: 'Droid账户' + droid: 'Droid账户', + bedrock: 'Bedrock账户' } // 拉取各平台账号列表 @@ -988,6 +1000,18 @@ router.get('/account-usage-trend', authenticateAdmin, async (req, res) => { platform: 'droid' } }) + } else if (group === 'bedrock') { + const result = await bedrockAccountService.getAllAccounts() + const bedrockAccounts = result?.success ? result.data : [] + accounts = bedrockAccounts.map((account) => { + const id = String(account.id || '') + const shortId = id ? id.slice(0, 8) : '未知' + return { + id, + name: account.name || `Bedrock账号 ${shortId}`, + platform: 'bedrock' + } + }) } if (!accounts || accounts.length === 0) { diff --git a/web/admin-spa/package-lock.json b/web/admin-spa/package-lock.json index 481df56a..9405609e 100644 --- a/web/admin-spa/package-lock.json +++ b/web/admin-spa/package-lock.json @@ -1157,6 +1157,7 @@ "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/lodash": "*" } @@ -1351,6 +1352,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1587,6 +1589,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", @@ -3060,13 +3063,15 @@ "version": "4.17.21", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash-es": { "version": "4.17.21", "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash-unified": { "version": "1.0.3", @@ -3618,6 +3623,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -3764,6 +3770,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -4028,6 +4035,7 @@ "integrity": "sha512-33xGNBsDJAkzt0PvninskHlWnTIPgDtTwhg0U38CUoNP/7H6wI2Cz6dUeoNPbjdTdsYTGuiFFASuUOWovH0SyQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -4525,6 +4533,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4915,6 +4924,7 @@ "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -5115,6 +5125,7 @@ "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.18.tgz", "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==", "license": "MIT", + "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.18", "@vue/compiler-sfc": "3.5.18", diff --git a/web/admin-spa/src/components/accounts/AccountUsageDetailModal.vue b/web/admin-spa/src/components/accounts/AccountUsageDetailModal.vue index 09da703b..a41be4fa 100644 --- a/web/admin-spa/src/components/accounts/AccountUsageDetailModal.vue +++ b/web/admin-spa/src/components/accounts/AccountUsageDetailModal.vue @@ -364,7 +364,8 @@ const platformLabelMap = { 'openai-responses': 'OpenAI Responses', gemini: 'Gemini', 'gemini-api': 'Gemini API', - droid: 'Droid' + droid: 'Droid', + bedrock: 'Claude AWS Bedrock' } const platformLabel = computed(() => platformLabelMap[props.account?.platform] || '未知平台')