1、增加调用 AI 视频生成能力和虚拟素材库管理的对外api
2、增加后台apikkey管理 3、增加apikey单独的模型定价 4、增加apikey调用情况 5、完善所有数据的注释增加
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
import logging
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.api.api_usage_log import ApiUsageLog
|
||||
from app.utils.id_gen import generate_id
|
||||
|
||||
logger = logging.getLogger("videogen")
|
||||
|
||||
|
||||
async def record_usage(
|
||||
db: AsyncSession,
|
||||
api_key_id: str,
|
||||
request_type: str,
|
||||
model_name: str,
|
||||
gen_type: str,
|
||||
status: str,
|
||||
task_id: str | None = None,
|
||||
credits_cost: float = 0.0,
|
||||
tokens_used: int = 0,
|
||||
request_duration_ms: int = 0,
|
||||
error_message: str | None = None,
|
||||
error_code: str | None = None,
|
||||
request_payload_json: str | None = None,
|
||||
price_action: str | None = None,
|
||||
resolution: str | None = None,
|
||||
duration: int | None = None,
|
||||
refund_amount: float | None = None,
|
||||
quota_before: float | None = None,
|
||||
quota_after: float | None = None,
|
||||
price_detail_json: str | None = None,
|
||||
) -> ApiUsageLog:
|
||||
"""记录一次 API 调用日志。"""
|
||||
# 确定 price_action
|
||||
if price_action:
|
||||
action = price_action
|
||||
elif status == "failed":
|
||||
action = "refund"
|
||||
else:
|
||||
action = "deduct"
|
||||
|
||||
log = ApiUsageLog(
|
||||
id=generate_id(),
|
||||
api_key_id=api_key_id,
|
||||
api_generation_task_id=task_id,
|
||||
price_action=action,
|
||||
request_type=request_type,
|
||||
model_name=model_name,
|
||||
gen_type=gen_type,
|
||||
resolution=resolution,
|
||||
duration=duration,
|
||||
credits_cost=credits_cost,
|
||||
refund_amount=refund_amount or 0.0,
|
||||
quota_before=quota_before,
|
||||
quota_after=quota_after,
|
||||
tokens_used=tokens_used,
|
||||
request_duration_ms=request_duration_ms,
|
||||
price_detail_json=price_detail_json,
|
||||
status=status,
|
||||
error_message=error_message,
|
||||
error_code=error_code,
|
||||
request_payload_json=request_payload_json,
|
||||
)
|
||||
db.add(log)
|
||||
await db.flush()
|
||||
return log
|
||||
|
||||
|
||||
async def list_usage_logs(
|
||||
db: AsyncSession,
|
||||
api_key_id: str | None = None,
|
||||
skip: int = 0,
|
||||
limit: int = 50,
|
||||
start_date: datetime | None = None,
|
||||
end_date: datetime | None = None,
|
||||
) -> tuple[int, list[ApiUsageLog]]:
|
||||
"""查询使用日志(分页+筛选)。"""
|
||||
query = select(ApiUsageLog)
|
||||
count_query = select(func.count(ApiUsageLog.id))
|
||||
|
||||
filters = []
|
||||
if api_key_id:
|
||||
filters.append(ApiUsageLog.api_key_id == api_key_id)
|
||||
if start_date:
|
||||
filters.append(ApiUsageLog.created_at >= start_date)
|
||||
if end_date:
|
||||
filters.append(ApiUsageLog.created_at <= end_date)
|
||||
|
||||
for f in filters:
|
||||
query = query.where(f)
|
||||
count_query = count_query.where(f)
|
||||
|
||||
total_result = await db.execute(count_query)
|
||||
total = total_result.scalar_one()
|
||||
|
||||
query = query.order_by(ApiUsageLog.created_at.desc()).offset(skip).limit(limit)
|
||||
result = await db.execute(query)
|
||||
logs = list(result.scalars().all())
|
||||
|
||||
return total, logs
|
||||
|
||||
|
||||
async def get_usage_summary(
|
||||
db: AsyncSession,
|
||||
api_key_id: str | None = None,
|
||||
days: int = 30,
|
||||
) -> dict:
|
||||
"""获取使用汇总统计。"""
|
||||
now = datetime.now(timezone.utc)
|
||||
start = now - timedelta(days=days)
|
||||
|
||||
query = select(
|
||||
func.count(ApiUsageLog.id).label("total_requests"),
|
||||
func.coalesce(func.sum(ApiUsageLog.credits_cost), 0).label("total_credits"),
|
||||
func.coalesce(func.sum(ApiUsageLog.tokens_used), 0).label("total_tokens"),
|
||||
func.coalesce(func.avg(ApiUsageLog.request_duration_ms), 0).label("avg_duration"),
|
||||
).where(ApiUsageLog.created_at >= start)
|
||||
|
||||
if api_key_id:
|
||||
query = query.where(ApiUsageLog.api_key_id == api_key_id)
|
||||
|
||||
result = await db.execute(query)
|
||||
row = result.one()
|
||||
|
||||
# 成功/失败统计
|
||||
success_query = select(func.count(ApiUsageLog.id)).where(
|
||||
ApiUsageLog.created_at >= start,
|
||||
ApiUsageLog.status == "success",
|
||||
)
|
||||
failed_query = select(func.count(ApiUsageLog.id)).where(
|
||||
ApiUsageLog.created_at >= start,
|
||||
ApiUsageLog.status == "failed",
|
||||
)
|
||||
if api_key_id:
|
||||
success_query = success_query.where(ApiUsageLog.api_key_id == api_key_id)
|
||||
failed_query = failed_query.where(ApiUsageLog.api_key_id == api_key_id)
|
||||
|
||||
success_result = await db.execute(success_query)
|
||||
failed_result = await db.execute(failed_query)
|
||||
|
||||
return {
|
||||
"total_requests": row.total_requests or 0,
|
||||
"total_credits_cost": float(row.total_credits or 0),
|
||||
"total_tokens_used": int(row.total_tokens or 0),
|
||||
"avg_duration_ms": int(row.avg_duration or 0),
|
||||
"success_count": success_result.scalar_one() or 0,
|
||||
"failed_count": failed_result.scalar_one() or 0,
|
||||
}
|
||||
Reference in New Issue
Block a user