chore: 升级到 Python 3.14 并现代化代码

- 升级 Docker 基础镜像从 Python 3.12 到 3.14
- 更新 pyproject.toml 支持 Python 3.13/3.14
- 移除 Python 3.8/3.9/3.10/3.11 分类器
- 更新 black 和 mypy 配置目标版本
- 将 get_event_loop() 替换为 get_running_loop() 加上 RuntimeError 处理
- 简化 compute_cost_sync 中的 asyncio.run 使用
- Dict/List/Tuple/Set → dict/list/tuple/set (PEP 585)
- Optional[T] → T | None (PEP 604)
- Union[A, B] → A | B (PEP 604)
- 移除废弃的 typing 导入
- 移除不必要的字符串引号注解
This commit is contained in:
AAEE86
2026-01-30 03:10:21 +08:00
parent 3e75bc8964
commit 24d24f6829
255 changed files with 4062 additions and 4173 deletions

View File

@@ -3,14 +3,14 @@ API密钥管理服务
"""
from datetime import datetime, timedelta, timezone
from typing import Any, Dict, List, Optional
from typing import Any
from sqlalchemy import func
from sqlalchemy.orm import Session
from src.core.crypto import crypto_service
from src.core.logger import logger
from src.models.database import ApiKey, Usage, User
from src.models.database import ApiKey, Usage
@@ -21,15 +21,15 @@ class ApiKeyService:
def create_api_key(
db: Session,
user_id: str, # UUID
name: Optional[str] = None,
allowed_providers: Optional[List[str]] = None,
allowed_api_formats: Optional[List[str]] = None,
allowed_models: Optional[List[str]] = None,
rate_limit: Optional[int] = None,
name: str | None = None,
allowed_providers: list[str] | None = None,
allowed_api_formats: list[str] | None = None,
allowed_models: list[str] | None = None,
rate_limit: int | None = None,
concurrent_limit: int = 5,
expire_days: Optional[int] = None,
expires_at: Optional[datetime] = None, # 直接传入过期时间,优先于 expire_days
initial_balance_usd: Optional[float] = None,
expire_days: int | None = None,
expires_at: datetime | None = None, # 直接传入过期时间,优先于 expire_days
initial_balance_usd: float | None = None,
is_standalone: bool = False,
auto_delete_on_expiry: bool = False,
) -> tuple[ApiKey, str]:
@@ -89,20 +89,20 @@ class ApiKeyService:
return api_key, key # 返回密钥对象和明文密钥
@staticmethod
def get_api_key(db: Session, key_id: str) -> Optional[ApiKey]: # UUID
def get_api_key(db: Session, key_id: str) -> ApiKey | None: # UUID
"""获取API密钥"""
return db.query(ApiKey).filter(ApiKey.id == key_id).first()
@staticmethod
def get_api_key_by_key(db: Session, key: str) -> Optional[ApiKey]:
def get_api_key_by_key(db: Session, key: str) -> ApiKey | None:
"""通过密钥字符串获取API密钥"""
key_hash = ApiKey.hash_key(key)
return db.query(ApiKey).filter(ApiKey.key_hash == key_hash).first()
@staticmethod
def list_user_api_keys(
db: Session, user_id: str, is_active: Optional[bool] = None # UUID
) -> List[ApiKey]:
db: Session, user_id: str, is_active: bool | None = None # UUID
) -> list[ApiKey]:
"""列出用户的所有API密钥不包括独立Key"""
query = db.query(ApiKey).filter(
ApiKey.user_id == user_id, ApiKey.is_standalone == False # 排除独立Key
@@ -114,7 +114,7 @@ class ApiKeyService:
return query.order_by(ApiKey.created_at.desc()).all()
@staticmethod
def list_standalone_api_keys(db: Session, is_active: Optional[bool] = None) -> List[ApiKey]:
def list_standalone_api_keys(db: Session, is_active: bool | None = None) -> list[ApiKey]:
"""列出所有独立余额Key仅管理员可用"""
query = db.query(ApiKey).filter(ApiKey.is_standalone == True)
@@ -124,7 +124,7 @@ class ApiKeyService:
return query.order_by(ApiKey.created_at.desc()).all()
@staticmethod
def update_api_key(db: Session, key_id: str, **kwargs) -> Optional[ApiKey]: # UUID
def update_api_key(db: Session, key_id: str, **kwargs) -> ApiKey | None: # UUID
"""更新API密钥"""
api_key = db.query(ApiKey).filter(ApiKey.id == key_id).first()
if not api_key:
@@ -186,7 +186,7 @@ class ApiKeyService:
return True
@staticmethod
def get_remaining_balance(api_key: ApiKey) -> Optional[float]:
def get_remaining_balance(api_key: ApiKey) -> float | None:
"""计算剩余余额仅用于独立Key
Returns:
@@ -203,7 +203,7 @@ class ApiKeyService:
return max(0, remaining) # 不能为负数
@staticmethod
def check_balance(api_key: ApiKey) -> tuple[bool, Optional[float]]:
def check_balance(api_key: ApiKey) -> tuple[bool, float | None]:
"""检查余额限制仅用于独立Key
Returns:
@@ -259,7 +259,7 @@ class ApiKeyService:
return is_allowed, api_key.rate_limit - request_count
@staticmethod
def add_balance(db: Session, key_id: str, amount_usd: float) -> Optional[ApiKey]:
def add_balance(db: Session, key_id: str, amount_usd: float) -> ApiKey | None:
"""为独立余额Key调整余额
Args:
@@ -355,9 +355,9 @@ class ApiKeyService:
def get_api_key_stats(
db: Session,
key_id: str, # UUID
start_date: Optional[datetime] = None,
end_date: Optional[datetime] = None,
) -> Dict[str, Any]:
start_date: datetime | None = None,
end_date: datetime | None = None,
) -> dict[str, Any]:
"""获取API密钥使用统计"""
api_key = db.query(ApiKey).filter(ApiKey.id == key_id).first()

View File

@@ -2,7 +2,6 @@
用户偏好设置服务
"""
from typing import Optional
from sqlalchemy.orm import Session
@@ -42,15 +41,15 @@ class PreferenceService:
def update_preferences(
db: Session,
user_id: str, # UUID
avatar_url: Optional[str] = None,
bio: Optional[str] = None,
default_provider_id: Optional[str] = None, # UUID
theme: Optional[str] = None,
language: Optional[str] = None,
timezone: Optional[str] = None,
email_notifications: Optional[bool] = None,
usage_alerts: Optional[bool] = None,
announcement_notifications: Optional[bool] = None,
avatar_url: str | None = None,
bio: str | None = None,
default_provider_id: str | None = None, # UUID
theme: str | None = None,
language: str | None = None,
timezone: str | None = None,
email_notifications: bool | None = None,
usage_alerts: bool | None = None,
announcement_notifications: bool | None = None,
) -> UserPreference:
"""更新用户偏好设置"""
preferences = PreferenceService.get_or_create_preferences(db, user_id)

View File

@@ -3,8 +3,8 @@
"""
import asyncio
from datetime import datetime, timedelta, timezone
from typing import Any, Dict, List, Optional
from datetime import datetime, timezone
from typing import Any
from sqlalchemy import and_, func
from sqlalchemy.orm import Session
@@ -25,15 +25,15 @@ class UserService:
@retry_on_database_error(max_retries=3)
def create_user(
db: Session,
email: Optional[str],
email: str | None,
username: str,
password: str,
role: UserRole = UserRole.USER,
quota_usd: Optional[float] = 10.0,
quota_usd: float | None = 10.0,
email_verified: bool = False,
allowed_providers: Optional[List[str]] = None,
allowed_api_formats: Optional[List[str]] = None,
allowed_models: Optional[List[str]] = None,
allowed_providers: list[str] | None = None,
allowed_api_formats: list[str] | None = None,
allowed_models: list[str] | None = None,
) -> User:
"""创建新用户quota_usd 为 None 表示无限制email 为 None 表示无邮箱"""
@@ -90,7 +90,7 @@ class UserService:
password: str,
api_key_name: str = "默认密钥",
role: UserRole = UserRole.USER,
quota_usd: Optional[float] = 10.0,
quota_usd: float | None = 10.0,
concurrent_limit: int = 5,
) -> tuple[User, ApiKey]:
"""
@@ -131,7 +131,7 @@ class UserService:
return user, api_key, plain_key
@staticmethod
def get_user(db: Session, user_id: str) -> Optional[User]:
def get_user(db: Session, user_id: str) -> User | None:
"""获取用户"""
import random
import time
@@ -152,7 +152,7 @@ class UserService:
raise e
@staticmethod
def get_user_by_email(db: Session, email: str) -> Optional[User]:
def get_user_by_email(db: Session, email: str) -> User | None:
"""通过邮箱获取用户"""
return db.query(User).filter(User.email == email).first()
@@ -161,9 +161,9 @@ class UserService:
db: Session,
skip: int = 0,
limit: int = 100,
role: Optional[UserRole] = None,
is_active: Optional[bool] = None,
) -> List[User]:
role: UserRole | None = None,
is_active: bool | None = None,
) -> list[User]:
"""列出用户"""
query = db.query(User)
@@ -176,7 +176,7 @@ class UserService:
@staticmethod
@transactional()
def update_user(db: Session, user_id: str, **kwargs) -> Optional[User]:
def update_user(db: Session, user_id: str, **kwargs) -> User | None:
"""更新用户信息"""
user = db.query(User).filter(User.id == user_id).first()
if not user:
@@ -322,8 +322,8 @@ class UserService:
def update_user_quota(
db: Session,
user_id: str,
quota_usd: Optional[float] = None,
) -> Optional[User]:
quota_usd: float | None = None,
) -> User | None:
"""更新用户配额"""
user = db.query(User).filter(User.id == user_id).first()
if not user:
@@ -345,9 +345,9 @@ class UserService:
def get_user_usage_stats(
db: Session,
user_id: str,
start_date: Optional[datetime] = None,
end_date: Optional[datetime] = None,
) -> Dict[str, Any]:
start_date: datetime | None = None,
end_date: datetime | None = None,
) -> dict[str, Any]:
"""获取用户使用统计"""
query = db.query(Usage).filter(Usage.user_id == user_id)
@@ -404,7 +404,7 @@ class UserService:
}
@staticmethod
def get_user_available_models(db: Session, user: User) -> List[Model]:
def get_user_available_models(db: Session, user: User) -> list[Model]:
"""获取用户可用的模型
通过 GlobalModel + Model 关联查询用户可用模型