refactor: 清理数据库字段命名歧义

- users 表:重命名 allowed_endpoints 为 allowed_api_formats(修正历史命名错误)
- api_keys 表:删除 allowed_endpoints 字段(未使用的功能)
- providers 表:删除 rate_limit 字段(与 rpm_limit 重复)
- usage 表:重命名 provider 为 provider_name(避免与 provider_id 外键混淆)

同步更新前后端所有相关代码
This commit is contained in:
fawney19
2026-01-07 19:53:32 +08:00
parent 6885cf1f6d
commit a12b43ce5c
24 changed files with 155 additions and 107 deletions

View File

@@ -73,7 +73,6 @@ async def create_provider(request: Request, db: Session = Depends(get_db)):
- `rpm_limit`: 每分钟请求数限制(可选)
- `provider_priority`: 提供商优先级(数字越小优先级越高,默认 100
- `is_active`: 是否启用(默认 true
- `rate_limit`: 速率限制配置(可选)
- `concurrent_limit`: 并发限制(可选)
- `config`: 额外配置信息JSON可选
@@ -110,7 +109,6 @@ async def update_provider(provider_id: str, request: Request, db: Session = Depe
- `rpm_limit`: 每分钟请求数限制
- `provider_priority`: 提供商优先级
- `is_active`: 是否启用
- `rate_limit`: 速率限制配置
- `concurrent_limit`: 并发限制
- `config`: 额外配置信息JSON
@@ -228,7 +226,6 @@ class AdminCreateProviderAdapter(AdminApiAdapter):
rpm_limit=validated_data.rpm_limit,
provider_priority=validated_data.provider_priority,
is_active=validated_data.is_active,
rate_limit=validated_data.rate_limit,
concurrent_limit=validated_data.concurrent_limit,
config=validated_data.config,
)

View File

@@ -684,7 +684,6 @@ class AdminExportConfigAdapter(AdminApiAdapter):
"rpm_limit": provider.rpm_limit,
"provider_priority": provider.provider_priority,
"is_active": provider.is_active,
"rate_limit": provider.rate_limit,
"concurrent_limit": provider.concurrent_limit,
"config": provider.config,
"endpoints": endpoints_data,
@@ -831,7 +830,6 @@ class AdminImportConfigAdapter(AdminApiAdapter):
"provider_priority", 100
)
existing_provider.is_active = prov_data.get("is_active", True)
existing_provider.rate_limit = prov_data.get("rate_limit")
existing_provider.concurrent_limit = prov_data.get(
"concurrent_limit"
)
@@ -856,7 +854,6 @@ class AdminImportConfigAdapter(AdminApiAdapter):
rpm_limit=prov_data.get("rpm_limit"),
provider_priority=prov_data.get("provider_priority", 100),
is_active=prov_data.get("is_active", True),
rate_limit=prov_data.get("rate_limit"),
concurrent_limit=prov_data.get("concurrent_limit"),
config=prov_data.get("config"),
)
@@ -1109,7 +1106,6 @@ class AdminExportUsersAdapter(AdminApiAdapter):
"balance_used_usd": key.balance_used_usd,
"current_balance_usd": key.current_balance_usd,
"allowed_providers": key.allowed_providers,
"allowed_endpoints": key.allowed_endpoints,
"allowed_api_formats": key.allowed_api_formats,
"allowed_models": key.allowed_models,
"rate_limit": key.rate_limit,
@@ -1146,7 +1142,7 @@ class AdminExportUsersAdapter(AdminApiAdapter):
"password_hash": user.password_hash,
"role": user.role.value if user.role else "user",
"allowed_providers": user.allowed_providers,
"allowed_endpoints": user.allowed_endpoints,
"allowed_api_formats": user.allowed_api_formats,
"allowed_models": user.allowed_models,
"model_capability_settings": user.model_capability_settings,
"quota_usd": user.quota_usd,
@@ -1238,7 +1234,6 @@ class AdminImportUsersAdapter(AdminApiAdapter):
balance_used_usd=key_data.get("balance_used_usd", 0.0),
current_balance_usd=key_data.get("current_balance_usd"),
allowed_providers=key_data.get("allowed_providers"),
allowed_endpoints=key_data.get("allowed_endpoints"),
allowed_api_formats=key_data.get("allowed_api_formats"),
allowed_models=key_data.get("allowed_models"),
rate_limit=key_data.get("rate_limit"),
@@ -1282,7 +1277,7 @@ class AdminImportUsersAdapter(AdminApiAdapter):
if user_data.get("role"):
existing_user.role = UserRole(user_data["role"])
existing_user.allowed_providers = user_data.get("allowed_providers")
existing_user.allowed_endpoints = user_data.get("allowed_endpoints")
existing_user.allowed_api_formats = user_data.get("allowed_api_formats")
existing_user.allowed_models = user_data.get("allowed_models")
existing_user.model_capability_settings = user_data.get(
"model_capability_settings"
@@ -1306,7 +1301,7 @@ class AdminImportUsersAdapter(AdminApiAdapter):
password_hash=user_data.get("password_hash", ""),
role=role,
allowed_providers=user_data.get("allowed_providers"),
allowed_endpoints=user_data.get("allowed_endpoints"),
allowed_api_formats=user_data.get("allowed_api_formats"),
allowed_models=user_data.get("allowed_models"),
model_capability_settings=user_data.get("model_capability_settings"),
quota_usd=user_data.get("quota_usd"),

View File

@@ -353,8 +353,8 @@ class AdminUsageByModelAdapter(AdminApiAdapter):
)
# 过滤掉 pending/streaming 状态的请求(尚未完成的请求不应计入统计)
query = query.filter(Usage.status.notin_(["pending", "streaming"]))
# 过滤掉 unknown/pending provider请求未到达任何提供商
query = query.filter(Usage.provider.notin_(["unknown", "pending"]))
# 过滤掉 unknown/pending provider_name(请求未到达任何提供商)
query = query.filter(Usage.provider_name.notin_(["unknown", "pending"]))
if self.start_date:
query = query.filter(Usage.created_at >= self.start_date)
@@ -565,8 +565,8 @@ class AdminUsageByApiFormatAdapter(AdminApiAdapter):
)
# 过滤掉 pending/streaming 状态的请求
query = query.filter(Usage.status.notin_(["pending", "streaming"]))
# 过滤掉 unknown/pending provider
query = query.filter(Usage.provider.notin_(["unknown", "pending"]))
# 过滤掉 unknown/pending provider_name
query = query.filter(Usage.provider_name.notin_(["unknown", "pending"]))
# 只统计有 api_format 的记录
query = query.filter(Usage.api_format.isnot(None))
@@ -765,8 +765,8 @@ class AdminUsageRecordsAdapter(AdminApiAdapter):
float(usage.rate_multiplier) if usage.rate_multiplier is not None else 1.0
)
# 提供商名称优先级:关联的 Provider 表 > usage.provider 字段
provider_name = usage.provider
# 提供商名称优先级:关联的 Provider 表 > usage.provider_name 字段
provider_name = usage.provider_name
if usage.provider_id and str(usage.provider_id) in provider_map:
provider_name = provider_map[str(usage.provider_id)]
@@ -881,7 +881,7 @@ class AdminUsageDetailAdapter(AdminApiAdapter):
"name": api_key.name if api_key else None,
"display": api_key.get_display_key() if api_key else None,
},
"provider": usage_record.provider,
"provider": usage_record.provider_name,
"api_format": usage_record.api_format,
"model": usage_record.model,
"target_model": usage_record.target_model,
@@ -934,7 +934,7 @@ class AdminUsageDetailAdapter(AdminApiAdapter):
# 尝试获取模型的阶梯配置(带来源信息)
cost_service = ModelCostService(db)
pricing_result = await cost_service.get_tiered_pricing_with_source_async(
usage_record.provider, usage_record.model
usage_record.provider_name, usage_record.model
)
if not pricing_result:

View File

@@ -246,7 +246,7 @@ class AdminCreateUserAdapter(AdminApiAdapter):
"username": user.username,
"role": user.role.value,
"allowed_providers": user.allowed_providers,
"allowed_endpoints": user.allowed_endpoints,
"allowed_api_formats": user.allowed_api_formats,
"allowed_models": user.allowed_models,
"quota_usd": user.quota_usd,
"used_usd": user.used_usd,
@@ -274,7 +274,7 @@ class AdminListUsersAdapter(AdminApiAdapter):
"username": u.username,
"role": u.role.value,
"allowed_providers": u.allowed_providers,
"allowed_endpoints": u.allowed_endpoints,
"allowed_api_formats": u.allowed_api_formats,
"allowed_models": u.allowed_models,
"quota_usd": u.quota_usd,
"used_usd": u.used_usd,
@@ -309,7 +309,7 @@ class AdminGetUserAdapter(AdminApiAdapter):
"username": user.username,
"role": user.role.value,
"allowed_providers": user.allowed_providers,
"allowed_endpoints": user.allowed_endpoints,
"allowed_api_formats": user.allowed_api_formats,
"allowed_models": user.allowed_models,
"quota_usd": user.quota_usd,
"used_usd": user.used_usd,
@@ -375,7 +375,7 @@ class AdminUpdateUserAdapter(AdminApiAdapter):
"username": user.username,
"role": user.role.value,
"allowed_providers": user.allowed_providers,
"allowed_endpoints": user.allowed_endpoints,
"allowed_api_formats": user.allowed_api_formats,
"allowed_models": user.allowed_models,
"quota_usd": user.quota_usd,
"used_usd": user.used_usd,

View File

@@ -528,7 +528,7 @@ class AuthCurrentUserAdapter(AuthenticatedApiAdapter):
"used_usd": user.used_usd,
"total_usd": user.total_usd,
"allowed_providers": user.allowed_providers,
"allowed_endpoints": user.allowed_endpoints,
"allowed_api_formats": user.allowed_api_formats,
"allowed_models": user.allowed_models,
"created_at": user.created_at.isoformat(),
"last_login_at": user.last_login_at.isoformat() if user.last_login_at else None,

View File

@@ -143,12 +143,13 @@ class AccessRestrictions:
allowed_api_formats = api_key.allowed_api_formats
# 如果 API Key 没有限制,检查 User 的限制
# 注意: User 没有 allowed_api_formats 字段
if user:
if allowed_providers is None and user.allowed_providers is not None:
allowed_providers = user.allowed_providers
if allowed_models is None and user.allowed_models is not None:
allowed_models = user.allowed_models
if allowed_api_formats is None and user.allowed_api_formats is not None:
allowed_api_formats = user.allowed_api_formats
return cls(
allowed_providers=allowed_providers,

View File

@@ -766,7 +766,7 @@ class DashboardProviderStatusAdapter(DashboardAdapter):
for provider in providers:
count = (
db.query(func.count(Usage.id))
.filter(and_(Usage.provider == provider.name, Usage.created_at >= since))
.filter(and_(Usage.provider_name == provider.name, Usage.created_at >= since))
.scalar()
)
entries.append(
@@ -854,7 +854,7 @@ class DashboardDailyStatsAdapter(DashboardAdapter):
.scalar() or 0
)
today_unique_providers = (
db.query(func.count(func.distinct(Usage.provider)))
db.query(func.count(func.distinct(Usage.provider_name)))
.filter(Usage.created_at >= today)
.scalar() or 0
)

View File

@@ -126,7 +126,9 @@ def _filter_formats_by_restrictions(
"""
if restrictions.allowed_api_formats is None:
return formats, None
filtered = [f for f in formats if f in restrictions.allowed_api_formats]
# 统一转为大写比较,兼容数据库中存储的大小写
allowed_upper = {f.upper() for f in restrictions.allowed_api_formats}
filtered = [f for f in formats if f.upper() in allowed_upper]
if not filtered:
logger.info(f"[Models] API Key 不允许访问格式 {api_format}")
return [], _build_empty_list_response(api_format)

View File

@@ -847,7 +847,7 @@ class GetUsageAdapter(AuthenticatedApiAdapter):
"records": [
{
"id": r.id,
"provider": r.provider,
"provider": r.provider_name,
"model": r.model,
"target_model": r.target_model, # 映射后的目标模型名
"api_format": r.api_format,