mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-24 07:47:13 +00:00
feat(admin): 新增账户余额/配额查询与展示
- 新增 accountBalanceService 与多 Provider 适配(Claude/Claude Console/OpenAI Responses/通用) - Redis 增加余额查询结果与本地统计缓存读写 - 管理端新增 /admin/accounts/balance 相关接口与汇总接口,并在应用启动时注册 Provider - 后台前端新增余额组件与 Dashboard 余额/配额汇总、低余额/高使用提示 - 补充 accountBalanceService 单元测试
This commit is contained in:
@@ -1521,6 +1521,99 @@ class RedisClient {
|
||||
return await this.client.del(key)
|
||||
}
|
||||
|
||||
// 💰 账户余额缓存(API 查询结果)
|
||||
async setAccountBalance(platform, accountId, balanceData, ttl = 3600) {
|
||||
const key = `account_balance:${platform}:${accountId}`
|
||||
|
||||
const payload = {
|
||||
balance:
|
||||
balanceData && balanceData.balance !== null && balanceData.balance !== undefined
|
||||
? String(balanceData.balance)
|
||||
: '',
|
||||
currency: balanceData?.currency || 'USD',
|
||||
lastRefreshAt: balanceData?.lastRefreshAt || new Date().toISOString(),
|
||||
queryMethod: balanceData?.queryMethod || 'api',
|
||||
status: balanceData?.status || 'success',
|
||||
errorMessage: balanceData?.errorMessage || balanceData?.error || '',
|
||||
rawData: balanceData?.rawData ? JSON.stringify(balanceData.rawData) : '',
|
||||
quota: balanceData?.quota ? JSON.stringify(balanceData.quota) : ''
|
||||
}
|
||||
|
||||
await this.client.hset(key, payload)
|
||||
await this.client.expire(key, ttl)
|
||||
}
|
||||
|
||||
async getAccountBalance(platform, accountId) {
|
||||
const key = `account_balance:${platform}:${accountId}`
|
||||
const [data, ttlSeconds] = await Promise.all([this.client.hgetall(key), this.client.ttl(key)])
|
||||
|
||||
if (!data || Object.keys(data).length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
let rawData = null
|
||||
if (data.rawData) {
|
||||
try {
|
||||
rawData = JSON.parse(data.rawData)
|
||||
} catch (error) {
|
||||
rawData = null
|
||||
}
|
||||
}
|
||||
|
||||
let quota = null
|
||||
if (data.quota) {
|
||||
try {
|
||||
quota = JSON.parse(data.quota)
|
||||
} catch (error) {
|
||||
quota = null
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
balance: data.balance ? parseFloat(data.balance) : null,
|
||||
currency: data.currency || 'USD',
|
||||
lastRefreshAt: data.lastRefreshAt || null,
|
||||
queryMethod: data.queryMethod || null,
|
||||
status: data.status || null,
|
||||
errorMessage: data.errorMessage || '',
|
||||
rawData,
|
||||
quota,
|
||||
ttlSeconds: Number.isFinite(ttlSeconds) ? ttlSeconds : null
|
||||
}
|
||||
}
|
||||
|
||||
// 📊 账户余额缓存(本地统计)
|
||||
async setLocalBalance(platform, accountId, statisticsData, ttl = 300) {
|
||||
const key = `account_balance_local:${platform}:${accountId}`
|
||||
|
||||
await this.client.hset(key, {
|
||||
estimatedBalance: JSON.stringify(statisticsData || {}),
|
||||
lastCalculated: new Date().toISOString()
|
||||
})
|
||||
await this.client.expire(key, ttl)
|
||||
}
|
||||
|
||||
async getLocalBalance(platform, accountId) {
|
||||
const key = `account_balance_local:${platform}:${accountId}`
|
||||
const data = await this.client.hgetall(key)
|
||||
|
||||
if (!data || !data.estimatedBalance) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(data.estimatedBalance)
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async deleteAccountBalance(platform, accountId) {
|
||||
const key = `account_balance:${platform}:${accountId}`
|
||||
const localKey = `account_balance_local:${platform}:${accountId}`
|
||||
await this.client.del(key, localKey)
|
||||
}
|
||||
|
||||
// 📈 系统统计
|
||||
async getSystemStats() {
|
||||
const keys = await Promise.all([
|
||||
|
||||
Reference in New Issue
Block a user