1、增加调用 AI 视频生成能力和虚拟素材库管理的对外api

2、增加后台apikkey管理
3、增加apikey单独的模型定价
4、增加apikey调用情况
5、完善所有数据的注释增加
This commit is contained in:
2026-08-06 13:13:28 +08:00
parent a55d4d649c
commit 0c511f3451
102 changed files with 13986 additions and 41 deletions
@@ -0,0 +1,31 @@
from app.schemas.admin_api.api_key import (
ApiKeyCreateRequest,
ApiKeyUpdateRequest,
ApiKeyResponse,
ApiKeyCreateResponse,
ApiKeyListItem,
ApiKeyListOut,
)
from app.schemas.admin_api.api_upscale import (
ApiUpscaleConfigData,
ApiUpscaleConfigSaveRequest,
ApiUpscaleConfigResponse,
)
from app.schemas.admin_api.api_usage import (
ApiUsageLogResponse,
ApiUsageSummaryResponse,
)
__all__ = [
"ApiKeyCreateRequest",
"ApiKeyUpdateRequest",
"ApiKeyResponse",
"ApiKeyCreateResponse",
"ApiKeyListItem",
"ApiKeyListOut",
"ApiUpscaleConfigData",
"ApiUpscaleConfigSaveRequest",
"ApiUpscaleConfigResponse",
"ApiUsageLogResponse",
"ApiUsageSummaryResponse",
]
@@ -0,0 +1,140 @@
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 ApiKeyListOut(BaseModel):
"""API Key 列表响应。"""
total: int
items: list[ApiKeyListItem]
@@ -0,0 +1,33 @@
from pydantic import BaseModel, ConfigDict, Field
from app.schemas.common import NaiveDatetime
class ApiModelPricingCreate(BaseModel):
"""创建 API 模型价格请求。支持 camelCase 和 snake_case 两种字段名。"""
model_config = ConfigDict(populate_by_name=True)
model_config_id: str = Field(
..., max_length=32, description="引擎ID", alias="modelConfigId",
)
gen_type: str = Field(default="video", max_length=16, description="image | video", alias="genType")
resolution: str = Field(..., max_length=16, description="分辨率")
price_ratio: float = Field(default=1.0, gt=0, description="价格系数", alias="priceRatio")
base_price: float = Field(default=0.0, ge=0, description="基础价格(元)", alias="basePrice")
per_second_price: float = Field(default=0.0, ge=0, description="每秒价格(元)", alias="perSecondPrice")
input_video_ratio: float = Field(default=1.0, ge=0, description="传入视频系数", alias="inputVideoRatio")
input_video_base_price: float = Field(default=0.0, ge=0, description="传入视频基础价(元)", alias="inputVideoBasePrice")
input_video_per_second_price: float = Field(default=0.0, ge=0, description="传入视频每秒价(元)", alias="inputVideoPerSecondPrice")
input_image_ratio: float = Field(default=1.0, ge=0, description="传入图片系数", alias="inputImageRatio")
input_image_base_price: float = Field(default=0.0, ge=0, description="传入图片基础价(元)", alias="inputImageBasePrice")
input_image_per_image_price: float = Field(default=0.0, ge=0, description="传入图片每张价(元)", alias="inputImagePerImagePrice")
class ApiModelPricingOut(ApiModelPricingCreate):
"""API 模型价格响应。"""
id: str
created_at: NaiveDatetime
model_config = ConfigDict(from_attributes=True, populate_by_name=True)
@@ -0,0 +1,38 @@
from pydantic import BaseModel, ConfigDict, Field
class ApiUpscaleRule(BaseModel):
"""API 超分规则。支持 camelCase 和 snake_case 两种字段名。"""
model_config = ConfigDict(populate_by_name=True)
target_resolution: str = Field(..., description="目标分辨率: 480p | 720p | 1080p | 2K | 4K", alias="targetResolution")
provider_generation_resolution: str = Field(..., description="供应商生成分辨率", alias="providerGenerationResolution")
processor_key: str = Field(
...,
description="处理器: local_ffmpeg_crop_v1 | volc_standard_v1 | volc_professional_v1 | volc_large_model_v1",
alias="processorKey",
)
enabled: bool = True
class ApiUpscaleConfigData(BaseModel):
"""API 超分配置数据。支持 camelCase 和 snake_case 两种字段名。"""
model_config = ConfigDict(populate_by_name=True)
enabled: bool = False
delete_source_after_success: bool = Field(True, alias="deleteSourceAfterSuccess")
rules: list[ApiUpscaleRule] = Field(default_factory=list)
class ApiUpscaleConfigSaveRequest(BaseModel):
"""保存 API 超分配置请求。"""
data: ApiUpscaleConfigData
class ApiUpscaleConfigResponse(BaseModel):
"""API 超分配置响应。"""
data: ApiUpscaleConfigData
@@ -0,0 +1,36 @@
from datetime import datetime
from pydantic import BaseModel
class ApiUsageLogResponse(BaseModel):
"""API 使用日志响应。"""
id: str
api_key_id: str
api_generation_task_id: str | None
request_type: str
model_name: str
gen_type: str
credits_cost: float
tokens_used: int
request_duration_ms: int
status: str
error_message: str | None
error_code: str | None
created_at: datetime
class Config:
from_attributes = True
class ApiUsageSummaryResponse(BaseModel):
"""API 使用汇总响应。"""
total_requests: int
total_credits_cost: float
total_tokens_used: int
success_count: int
failed_count: int
avg_duration_ms: int
items: list[ApiUsageLogResponse]
@@ -0,0 +1,33 @@
from __future__ import annotations
from pydantic import BaseModel, ConfigDict, Field
class VpV3QuotaConfigData(BaseModel):
"""后台保存虚拟素材库配额。"""
project_limit: int = Field(0, ge=0, description="虚拟项目上限,0=不可创建")
asset_limit: int = Field(0, ge=0, description="虚拟素材总数上限,0=不可上传")
storage_mb_limit: int = Field(0, ge=0, description="存储上限 MB0=不可上传文件")
remark: str | None = Field(None, max_length=500, description="后台备注")
class VpV3QuotaConfigResponse(BaseModel):
"""虚拟素材库配额响应。"""
model_config = ConfigDict(extra="ignore")
api_key_id: str = Field(description="API Key ID")
# 上限
project_limit: int = Field(0, description="虚拟项目上限")
asset_limit: int = Field(0, description="虚拟素材上限")
storage_mb_limit: int = Field(0, description="存储上限 MB")
remark: str | None = Field(None, description="备注")
# 已使用
project_used: int = Field(0, description="已创建项目数")
asset_used: int = Field(0, description="已上传素材数")
storage_mb_used: float = Field(0.0, description="已使用存储 MB")
enabled: bool = Field(False, description="是否启用(任一上限 > 0")