添加claude账号维度计算token费用

This commit is contained in:
leslie
2025-07-25 21:27:17 +08:00
parent 4325de90e1
commit 5522967792
5 changed files with 304 additions and 9 deletions

View File

@@ -234,18 +234,27 @@ class ApiKeyService {
}
}
// 📊 记录使用情况支持缓存token
async recordUsage(keyId, inputTokens = 0, outputTokens = 0, cacheCreateTokens = 0, cacheReadTokens = 0, model = 'unknown') {
// 📊 记录使用情况支持缓存token和账户级别统计
async recordUsage(keyId, inputTokens = 0, outputTokens = 0, cacheCreateTokens = 0, cacheReadTokens = 0, model = 'unknown', accountId = null) {
try {
const totalTokens = inputTokens + outputTokens + cacheCreateTokens + cacheReadTokens;
// 记录API Key级别的使用统计
await redis.incrementTokenUsage(keyId, totalTokens, inputTokens, outputTokens, cacheCreateTokens, cacheReadTokens, model);
// 更新最后使用时间(性能优化:只在实际使用时更新)
// 获取API Key数据以确定关联的账户
const keyData = await redis.getApiKey(keyId);
if (keyData && Object.keys(keyData).length > 0) {
// 更新最后使用时间
keyData.lastUsedAt = new Date().toISOString();
// 使用记录时不需要重新建立哈希映射
await redis.setApiKey(keyId, keyData);
// 记录账户级别的使用统计
const claudeAccountId = accountId || keyData.claudeAccountId;
if (claudeAccountId) {
await redis.incrementAccountUsage(claudeAccountId, totalTokens, inputTokens, outputTokens, cacheCreateTokens, cacheReadTokens, model);
logger.database(`📊 Recorded account usage: ${claudeAccountId} - ${totalTokens} tokens`);
}
}
const logParts = [`Model: ${model}`, `Input: ${inputTokens}`, `Output: ${outputTokens}`];
@@ -274,6 +283,16 @@ class ApiKeyService {
return await redis.getUsageStats(keyId);
}
// 📊 获取账户使用统计
async getAccountUsageStats(accountId) {
return await redis.getAccountUsageStats(accountId);
}
// 📈 获取所有账户使用统计
async getAllAccountsUsageStats() {
return await redis.getAllAccountsUsageStats();
}
// 🧹 清理过期的API Keys
async cleanupExpiredKeys() {

View File

@@ -181,6 +181,8 @@ class ClaudeRelayService {
logger.info(`✅ API request completed - Key: ${apiKeyData.name}, Account: ${accountId}, Model: ${requestBody.model}, Input: ~${Math.round(inputTokens)} tokens, Output: ~${Math.round(outputTokens)} tokens`);
// 在响应中添加accountId以便调用方记录账户级别统计
response.accountId = accountId;
return response;
} catch (error) {
logger.error(`❌ Claude relay request failed for key: ${apiKeyData.name || apiKeyData.id}:`, error.message);
@@ -619,7 +621,10 @@ class ClaudeRelayService {
const proxyAgent = await this._getProxyAgent(accountId);
// 发送流式请求并捕获usage数据
return await this._makeClaudeStreamRequestWithUsageCapture(processedBody, accessToken, proxyAgent, clientHeaders, responseStream, usageCallback, accountId, sessionHash, streamTransformer, options);
return await this._makeClaudeStreamRequestWithUsageCapture(processedBody, accessToken, proxyAgent, clientHeaders, responseStream, (usageData) => {
// 在usageCallback中添加accountId
usageCallback({ ...usageData, accountId });
}, accountId, sessionHash, streamTransformer, options);
} catch (error) {
logger.error('❌ Claude stream relay with usage capture failed:', error);
throw error;