from datetime import datetime from pydantic import BaseModel, ConfigDict, Field, field_validator # 保留供其他地方使用 def _empty_to_null(value): """将空字符串转为 None,避免 Pydantic 校验失败。""" if value == "" or value == "null": return None return value class ApiKeyCallableModel(BaseModel): """API Key 可调用模型配置。支持 camelCase 和 snake_case 两种字段名。""" model_config = ConfigDict(populate_by_name=True) engine_type: str = Field(..., description="video | image", alias="engineType") engine_id: str = Field(..., description="引擎ID", alias="engineId") model_name: str = Field(..., description="模型名称", alias="modelName") class ApiKeyCreateRequest(BaseModel): """创建 API Key 请求。支持 camelCase 和 snake_case 两种字段名。""" model_config = ConfigDict(populate_by_name=True) company_name: str = Field(..., max_length=128, description="公司名称", alias="companyName") description: str | None = Field(None, description="备注") callable_models: list[ApiKeyCallableModel] = Field( default_factory=list, description="可调用模型列表", alias="callableModels", ) quota_limit: float | None = Field(None, description="配额总量,NULL=无限", alias="quotaLimit") quota_cycle: str | None = Field(None, description="daily | monthly | one_time | NULL=无限", alias="quotaCycle") valid_from: datetime | None = Field(None, description="生效时间", alias="validFrom") valid_until: datetime | None = Field(None, description="过期时间", alias="validUntil") max_concurrent_video_tasks: int | None = Field( None, description="最大并发视频任务数", alias="maxConcurrentVideoTasks", ) @field_validator("valid_from", "valid_until", mode="before") @classmethod def empty_str_to_none(cls, v): if v == "" or v == "null" or v == 0: return None return v class ApiKeyUpdateRequest(BaseModel): """更新 API Key 请求。支持 camelCase 和 snake_case 两种字段名。""" model_config = ConfigDict(populate_by_name=True) company_name: str | None = Field(None, max_length=128, alias="companyName") description: str | None = None callable_models: list[ApiKeyCallableModel] | None = Field(None, alias="callableModels") quota_limit: float | None = Field(None, alias="quotaLimit") quota_cycle: str | None = Field(None, alias="quotaCycle") valid_from: datetime | None = Field(None, alias="validFrom") valid_until: datetime | None = Field(None, alias="validUntil") max_concurrent_video_tasks: int | None = Field(None, alias="maxConcurrentVideoTasks") is_active: bool | None = Field(None, alias="isActive") @field_validator("valid_from", "valid_until", mode="before") @classmethod def empty_str_to_none(cls, v): if v == "" or v == "null" or v == 0: return None return v class ApiKeyResponse(BaseModel): """API Key 详情响应。""" id: str company_name: str api_key_prefix: str = Field(..., description="Key 前缀,如 vk_xxxx****") description: str | None callable_models: list[ApiKeyCallableModel] quota_limit: float | None quota_cycle: str | None quota_used: float valid_from: datetime | None valid_until: datetime | None max_concurrent_video_tasks: int | None is_active: bool last_used_at: datetime | None created_at: datetime updated_at: datetime model_config = ConfigDict(from_attributes=True) class ApiKeyCreateResponse(BaseModel): """创建 API Key 响应(包含完整明文 Key,仅此一次)。""" id: str company_name: str api_key: str = Field(..., description="完整 API Key,仅创建时返回一次") api_key_prefix: str valid_until: datetime | None created_at: datetime class ApiKeyRevealResponse(BaseModel): """揭秘 API Key 响应(随时可获取明文)。""" id: str company_name: str api_key: str = Field(..., description="完整 API Key") api_key_prefix: str class ApiKeyListItem(BaseModel): """API Key 列表项。""" id: str company_name: str api_key_prefix: str description: str | None callable_models: list[ApiKeyCallableModel] = [] quota_limit: float | None quota_cycle: str | None quota_used: float is_active: bool valid_from: datetime | None valid_until: datetime | None max_concurrent_video_tasks: int | None last_used_at: datetime | None created_at: datetime model_config = ConfigDict(from_attributes=True) class ApiKeyQuotaAdjustRequest(BaseModel): """配额调整请求。支持 camelCase 和 snake_case 两种字段名。""" model_config = ConfigDict(populate_by_name=True) action: str = Field( ..., pattern=r"^(adjust|reset_usage|set_limit|change_cycle)$", description="adjust=增加总额 | reset_usage=重置已用 | set_limit=设置限额 | change_cycle=修改周期", ) quota_limit_delta: float | None = Field(None, ge=0, description="增加总额时的增量", alias="quotaLimitDelta") quota_limit: float | None = Field(None, description="设置新限额时的值(NULL=无限)", alias="quotaLimit") quota_cycle: str | None = Field(None, description="修改周期时的值", alias="quotaCycle") reason: str | None = Field(None, max_length=500, description="调整原因/备注") class ApiKeyListOut(BaseModel): """API Key 列表响应。""" total: int items: list[ApiKeyListItem]