2026-01-08 02:34:59 +08:00
|
|
|
|
"""
|
|
|
|
|
|
Provider 缓存服务 - 减少 Provider 和 ProviderAPIKey 查询
|
|
|
|
|
|
|
|
|
|
|
|
用于缓存 Provider 的 billing_type 和 ProviderAPIKey 的 rate_multiplier,
|
|
|
|
|
|
这些数据在 UsageService.record_usage() 中被频繁查询但变化不频繁。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
2026-01-30 14:30:57 +08:00
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
2026-01-08 02:34:59 +08:00
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
|
|
|
|
|
|
from src.config.constants import CacheTTL
|
|
|
|
|
|
from src.core.cache_service import CacheService
|
|
|
|
|
|
from src.core.enums import ProviderBillingType
|
|
|
|
|
|
from src.core.logger import logger
|
|
|
|
|
|
from src.models.database import Provider, ProviderAPIKey
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProviderCacheService:
|
|
|
|
|
|
"""Provider 缓存服务
|
|
|
|
|
|
|
|
|
|
|
|
提供 Provider 和 ProviderAPIKey 的缓存查询功能,减少数据库访问。
|
|
|
|
|
|
主要用于 UsageService 中获取费率倍数和计费类型。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
CACHE_TTL = CacheTTL.PROVIDER # 5 分钟
|
|
|
|
|
|
|
2026-01-15 11:45:46 +08:00
|
|
|
|
@staticmethod
|
|
|
|
|
|
def compute_rate_multiplier(
|
2026-01-30 03:10:21 +08:00
|
|
|
|
rate_multipliers: dict | None,
|
|
|
|
|
|
api_format: str | None = None,
|
2026-01-15 11:45:46 +08:00
|
|
|
|
) -> float:
|
|
|
|
|
|
"""
|
|
|
|
|
|
计算 rate_multiplier 的纯函数(无数据库/缓存依赖)
|
|
|
|
|
|
|
2026-01-16 17:53:27 +08:00
|
|
|
|
返回指定 API 格式的倍率,如果没有则返回 1.0。
|
2026-01-15 11:45:46 +08:00
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
rate_multipliers: 按 API 格式的倍率配置字典
|
|
|
|
|
|
api_format: API 格式(可选),如 "CLAUDE"、"OPENAI"
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
计算后的 rate_multiplier
|
|
|
|
|
|
"""
|
|
|
|
|
|
if api_format and rate_multipliers:
|
2026-02-01 17:28:00 +08:00
|
|
|
|
format_key = str(api_format).strip().lower()
|
|
|
|
|
|
if format_key in rate_multipliers:
|
|
|
|
|
|
return float(rate_multipliers[format_key])
|
2026-01-16 17:53:27 +08:00
|
|
|
|
return 1.0
|
2026-01-15 11:45:46 +08:00
|
|
|
|
|
2026-01-08 02:34:59 +08:00
|
|
|
|
@staticmethod
|
|
|
|
|
|
async def get_provider_api_key_rate_multiplier(
|
2026-01-30 03:10:21 +08:00
|
|
|
|
db: Session, provider_api_key_id: str, api_format: str | None = None
|
|
|
|
|
|
) -> float | None:
|
2026-01-08 02:34:59 +08:00
|
|
|
|
"""
|
|
|
|
|
|
获取 ProviderAPIKey 的 rate_multiplier(带缓存)
|
|
|
|
|
|
|
2026-01-10 18:43:53 +08:00
|
|
|
|
优先返回指定 API 格式的倍率,如果没有则返回默认倍率。
|
|
|
|
|
|
|
2026-01-08 02:34:59 +08:00
|
|
|
|
Args:
|
|
|
|
|
|
db: 数据库会话
|
|
|
|
|
|
provider_api_key_id: ProviderAPIKey ID
|
2026-01-10 18:43:53 +08:00
|
|
|
|
api_format: API 格式(可选),如 "CLAUDE"、"OPENAI"
|
2026-01-08 02:34:59 +08:00
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
rate_multiplier 或 None(如果找不到)
|
|
|
|
|
|
"""
|
2026-01-10 18:43:53 +08:00
|
|
|
|
# 缓存键包含 api_format
|
2026-02-01 17:28:00 +08:00
|
|
|
|
format_suffix = str(api_format).strip().lower() if api_format else "default"
|
2026-01-10 18:43:53 +08:00
|
|
|
|
cache_key = f"provider_api_key:rate_multiplier:{provider_api_key_id}:{format_suffix}"
|
2026-01-08 02:34:59 +08:00
|
|
|
|
|
|
|
|
|
|
# 1. 尝试从缓存获取
|
|
|
|
|
|
cached_data = await CacheService.get(cache_key)
|
|
|
|
|
|
if cached_data is not None:
|
2026-02-01 17:28:00 +08:00
|
|
|
|
logger.debug(
|
|
|
|
|
|
f"ProviderAPIKey rate_multiplier 缓存命中: {provider_api_key_id[:8]}... format={format_suffix}"
|
|
|
|
|
|
)
|
2026-01-08 02:34:59 +08:00
|
|
|
|
# 缓存的 "NOT_FOUND" 表示数据库中不存在
|
|
|
|
|
|
if cached_data == "NOT_FOUND":
|
|
|
|
|
|
return None
|
|
|
|
|
|
return float(cached_data)
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 缓存未命中,查询数据库
|
|
|
|
|
|
provider_key = (
|
2026-01-16 17:53:27 +08:00
|
|
|
|
db.query(ProviderAPIKey.rate_multipliers)
|
2026-01-08 02:34:59 +08:00
|
|
|
|
.filter(ProviderAPIKey.id == provider_api_key_id)
|
|
|
|
|
|
.first()
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-01-10 18:43:53 +08:00
|
|
|
|
# 3. 计算倍率并写入缓存
|
2026-01-08 02:34:59 +08:00
|
|
|
|
if provider_key:
|
2026-01-15 11:45:46 +08:00
|
|
|
|
rate_multiplier = ProviderCacheService.compute_rate_multiplier(
|
2026-01-16 17:53:27 +08:00
|
|
|
|
provider_key.rate_multipliers, api_format
|
2026-01-15 11:45:46 +08:00
|
|
|
|
)
|
2026-01-10 18:43:53 +08:00
|
|
|
|
|
2026-01-08 02:34:59 +08:00
|
|
|
|
await CacheService.set(
|
|
|
|
|
|
cache_key, rate_multiplier, ttl_seconds=ProviderCacheService.CACHE_TTL
|
|
|
|
|
|
)
|
2026-02-01 17:28:00 +08:00
|
|
|
|
logger.debug(
|
|
|
|
|
|
f"ProviderAPIKey rate_multiplier 已缓存: {provider_api_key_id[:8]}... format={format_suffix} value={rate_multiplier}"
|
|
|
|
|
|
)
|
2026-01-08 02:34:59 +08:00
|
|
|
|
return rate_multiplier
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 缓存负结果
|
|
|
|
|
|
await CacheService.set(
|
|
|
|
|
|
cache_key, "NOT_FOUND", ttl_seconds=ProviderCacheService.CACHE_TTL
|
|
|
|
|
|
)
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
async def get_provider_billing_type(
|
|
|
|
|
|
db: Session, provider_id: str
|
2026-01-30 03:10:21 +08:00
|
|
|
|
) -> ProviderBillingType | None:
|
2026-01-08 02:34:59 +08:00
|
|
|
|
"""
|
|
|
|
|
|
获取 Provider 的 billing_type(带缓存)
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
db: 数据库会话
|
|
|
|
|
|
provider_id: Provider ID
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
billing_type 或 None(如果找不到)
|
|
|
|
|
|
"""
|
|
|
|
|
|
cache_key = f"provider:billing_type:{provider_id}"
|
|
|
|
|
|
|
|
|
|
|
|
# 1. 尝试从缓存获取
|
|
|
|
|
|
cached_data = await CacheService.get(cache_key)
|
|
|
|
|
|
if cached_data is not None:
|
|
|
|
|
|
logger.debug(f"Provider billing_type 缓存命中: {provider_id[:8]}...")
|
|
|
|
|
|
if cached_data == "NOT_FOUND":
|
|
|
|
|
|
return None
|
|
|
|
|
|
try:
|
|
|
|
|
|
return ProviderBillingType(cached_data)
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
# 缓存值无效,删除并重新查询
|
|
|
|
|
|
await CacheService.delete(cache_key)
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 缓存未命中,查询数据库
|
2026-02-01 17:28:00 +08:00
|
|
|
|
provider = db.query(Provider.billing_type).filter(Provider.id == provider_id).first()
|
2026-01-08 02:34:59 +08:00
|
|
|
|
|
|
|
|
|
|
# 3. 写入缓存
|
|
|
|
|
|
if provider:
|
|
|
|
|
|
billing_type = provider.billing_type
|
|
|
|
|
|
await CacheService.set(
|
|
|
|
|
|
cache_key, billing_type.value, ttl_seconds=ProviderCacheService.CACHE_TTL
|
|
|
|
|
|
)
|
|
|
|
|
|
logger.debug(f"Provider billing_type 已缓存: {provider_id[:8]}...")
|
|
|
|
|
|
return billing_type
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 缓存负结果
|
|
|
|
|
|
await CacheService.set(
|
|
|
|
|
|
cache_key, "NOT_FOUND", ttl_seconds=ProviderCacheService.CACHE_TTL
|
|
|
|
|
|
)
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
async def get_rate_multiplier_and_free_tier(
|
|
|
|
|
|
db: Session,
|
2026-01-30 03:10:21 +08:00
|
|
|
|
provider_api_key_id: str | None,
|
|
|
|
|
|
provider_id: str | None,
|
|
|
|
|
|
api_format: str | None = None,
|
|
|
|
|
|
) -> tuple[float, bool]:
|
2026-01-08 02:34:59 +08:00
|
|
|
|
"""
|
|
|
|
|
|
获取费率倍数和是否免费套餐(带缓存)
|
|
|
|
|
|
|
|
|
|
|
|
这是 UsageService._get_rate_multiplier_and_free_tier 的缓存版本。
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
db: 数据库会话
|
|
|
|
|
|
provider_api_key_id: ProviderAPIKey ID(可选)
|
|
|
|
|
|
provider_id: Provider ID(可选)
|
2026-01-10 18:43:53 +08:00
|
|
|
|
api_format: API 格式(可选),用于获取按格式配置的倍率
|
2026-01-08 02:34:59 +08:00
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
(rate_multiplier, is_free_tier) 元组
|
|
|
|
|
|
"""
|
|
|
|
|
|
actual_rate_multiplier = 1.0
|
|
|
|
|
|
is_free_tier = False
|
|
|
|
|
|
|
2026-01-10 18:43:53 +08:00
|
|
|
|
# 获取费率倍数(支持按 API 格式查询)
|
2026-01-08 02:34:59 +08:00
|
|
|
|
if provider_api_key_id:
|
|
|
|
|
|
rate_multiplier = await ProviderCacheService.get_provider_api_key_rate_multiplier(
|
2026-01-10 18:43:53 +08:00
|
|
|
|
db, provider_api_key_id, api_format
|
2026-01-08 02:34:59 +08:00
|
|
|
|
)
|
|
|
|
|
|
if rate_multiplier is not None:
|
|
|
|
|
|
actual_rate_multiplier = rate_multiplier
|
|
|
|
|
|
|
|
|
|
|
|
# 获取计费类型
|
|
|
|
|
|
if provider_id:
|
|
|
|
|
|
billing_type = await ProviderCacheService.get_provider_billing_type(db, provider_id)
|
|
|
|
|
|
if billing_type == ProviderBillingType.FREE_TIER:
|
|
|
|
|
|
is_free_tier = True
|
|
|
|
|
|
|
|
|
|
|
|
return actual_rate_multiplier, is_free_tier
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
async def invalidate_provider_api_key_cache(provider_api_key_id: str) -> None:
|
2026-01-10 18:43:53 +08:00
|
|
|
|
"""清除 ProviderAPIKey 缓存(包括所有 API 格式的缓存)"""
|
|
|
|
|
|
# 使用模式匹配删除所有格式的缓存
|
2026-02-01 17:28:00 +08:00
|
|
|
|
await CacheService.delete_pattern(
|
|
|
|
|
|
f"provider_api_key:rate_multiplier:{provider_api_key_id}:*"
|
|
|
|
|
|
)
|
2026-01-08 02:34:59 +08:00
|
|
|
|
logger.debug(f"ProviderAPIKey 缓存已清除: {provider_api_key_id[:8]}...")
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
async def invalidate_provider_cache(provider_id: str) -> None:
|
|
|
|
|
|
"""清除 Provider 缓存"""
|
|
|
|
|
|
await CacheService.delete(f"provider:billing_type:{provider_id}")
|
|
|
|
|
|
logger.debug(f"Provider 缓存已清除: {provider_id[:8]}...")
|