This commit is contained in:
SunSeekerX
2026-01-19 20:24:47 +08:00
parent 12fd5e1cb4
commit 76ecbe18a5
98 changed files with 8182 additions and 1896 deletions

View File

@@ -0,0 +1,227 @@
/**
* 服务倍率配置服务
* 管理不同服务的消费倍率,以 Claude 为基准(倍率 1.0
* 用于聚合 Key 的虚拟额度计算
*/
const redis = require('../models/redis')
const logger = require('../utils/logger')
class ServiceRatesService {
constructor() {
this.CONFIG_KEY = 'system:service_rates'
this.cachedRates = null
this.cacheExpiry = 0
this.CACHE_TTL = 60 * 1000 // 1分钟缓存
}
/**
* 获取默认倍率配置
*/
getDefaultRates() {
return {
baseService: 'claude',
rates: {
claude: 1.0, // 基准1 USD = 1 CC额度
codex: 1.0,
gemini: 1.0,
droid: 1.0,
bedrock: 1.0,
azure: 1.0,
ccr: 1.0
},
updatedAt: null,
updatedBy: null
}
}
/**
* 获取倍率配置(带缓存)
*/
async getRates() {
try {
// 检查缓存
if (this.cachedRates && Date.now() < this.cacheExpiry) {
return this.cachedRates
}
const configStr = await redis.client.get(this.CONFIG_KEY)
if (!configStr) {
const defaultRates = this.getDefaultRates()
this.cachedRates = defaultRates
this.cacheExpiry = Date.now() + this.CACHE_TTL
return defaultRates
}
const storedConfig = JSON.parse(configStr)
// 合并默认值,确保新增服务有默认倍率
const defaultRates = this.getDefaultRates()
storedConfig.rates = {
...defaultRates.rates,
...storedConfig.rates
}
this.cachedRates = storedConfig
this.cacheExpiry = Date.now() + this.CACHE_TTL
return storedConfig
} catch (error) {
logger.error('获取服务倍率配置失败:', error)
return this.getDefaultRates()
}
}
/**
* 保存倍率配置
*/
async saveRates(config, updatedBy = 'admin') {
try {
const defaultRates = this.getDefaultRates()
// 验证配置
this.validateRates(config)
const newConfig = {
baseService: config.baseService || defaultRates.baseService,
rates: {
...defaultRates.rates,
...config.rates
},
updatedAt: new Date().toISOString(),
updatedBy
}
await redis.client.set(this.CONFIG_KEY, JSON.stringify(newConfig))
// 清除缓存
this.cachedRates = null
this.cacheExpiry = 0
logger.info(`✅ 服务倍率配置已更新 by ${updatedBy}`)
return newConfig
} catch (error) {
logger.error('保存服务倍率配置失败:', error)
throw error
}
}
/**
* 验证倍率配置
*/
validateRates(config) {
if (!config || typeof config !== 'object') {
throw new Error('无效的配置格式')
}
if (config.rates) {
for (const [service, rate] of Object.entries(config.rates)) {
if (typeof rate !== 'number' || rate <= 0) {
throw new Error(`服务 ${service} 的倍率必须是正数`)
}
}
}
}
/**
* 获取单个服务的倍率
*/
async getServiceRate(service) {
const config = await this.getRates()
return config.rates[service] || 1.0
}
/**
* 计算消费的 CC 额度
* @param {number} costUSD - 真实成本USD
* @param {string} service - 服务类型
* @returns {number} CC 额度消耗
*/
async calculateQuotaConsumption(costUSD, service) {
const rate = await this.getServiceRate(service)
return costUSD * rate
}
/**
* 根据模型名称获取服务类型
*/
getServiceFromModel(model) {
if (!model) {
return 'claude'
}
const modelLower = model.toLowerCase()
// Claude 系列
if (
modelLower.includes('claude') ||
modelLower.includes('anthropic') ||
modelLower.includes('opus') ||
modelLower.includes('sonnet') ||
modelLower.includes('haiku')
) {
return 'claude'
}
// OpenAI / Codex 系列
if (
modelLower.includes('gpt') ||
modelLower.includes('o1') ||
modelLower.includes('o3') ||
modelLower.includes('o4') ||
modelLower.includes('codex') ||
modelLower.includes('davinci') ||
modelLower.includes('curie') ||
modelLower.includes('babbage') ||
modelLower.includes('ada')
) {
return 'codex'
}
// Gemini 系列
if (
modelLower.includes('gemini') ||
modelLower.includes('palm') ||
modelLower.includes('bard')
) {
return 'gemini'
}
// Droid 系列
if (modelLower.includes('droid') || modelLower.includes('factory')) {
return 'droid'
}
// Bedrock 系列(通常带有 aws 或特定前缀)
if (
modelLower.includes('bedrock') ||
modelLower.includes('amazon') ||
modelLower.includes('titan')
) {
return 'bedrock'
}
// Azure 系列
if (modelLower.includes('azure')) {
return 'azure'
}
// 默认返回 claude
return 'claude'
}
/**
* 获取所有支持的服务列表
*/
async getAvailableServices() {
const config = await this.getRates()
return Object.keys(config.rates)
}
/**
* 清除缓存(用于测试或强制刷新)
*/
clearCache() {
this.cachedRates = null
this.cacheExpiry = 0
}
}
module.exports = new ServiceRatesService()