mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
perf: 全栈查询优化、前端缓存去重与页面可见性优化
后端: - SQL count 查询统一改用 func.count() 子查询替代 query.count() - Dashboard/Audit 等页面多次独立查询合并为单次聚合查询 - Provider summary 列表改为批量查询消除 N+1 问题 - DailyStats 逐天循环查询改为 CASE 分桶单次查询 - 使用 load_only() 减少不必要的列加载 - cache_decorator 支持嵌套属性路径解析(dotted vary_by) - 多个管理/公共端点新增 @cache_result 缓存装饰 前端: - cache.ts 新增 in-flight 请求复用、dedupedRequest、buildCacheKey - 大量 API 调用添加前端缓存或去重 - 多个页面定时器在标签页隐藏时暂停、可见时恢复 - Auth 检查从 setInterval 改为 storage + visibilitychange 事件驱动 - 请求竞态防护(requestId 模式) 数据库: - Usage 表新增 idx_usage_status_user_created 复合索引
This commit is contained in:
@@ -18,11 +18,13 @@ from src.api.base.context import ApiRequestContext
|
||||
from src.api.base.pipeline import ApiRequestPipeline
|
||||
from src.api.dashboard.routes import DashboardAdapter
|
||||
from src.clients.http_client import HTTPClientPool
|
||||
from src.config.constants import CacheTTL
|
||||
from src.core.crypto import crypto_service
|
||||
from src.core.enums import UserRole
|
||||
from src.core.logger import logger
|
||||
from src.database import get_db
|
||||
from src.models.database import Provider, ProviderAPIKey, ProviderEndpoint, User, VideoTask
|
||||
from src.utils.cache_decorator import cache_result
|
||||
|
||||
router = APIRouter(prefix="/api/admin/video-tasks", tags=["Admin - Video Tasks"])
|
||||
pipeline = ApiRequestPipeline()
|
||||
@@ -247,6 +249,12 @@ class VideoTaskListAdapter(DashboardAdapter):
|
||||
page: int
|
||||
page_size: int
|
||||
|
||||
@cache_result(
|
||||
key_prefix="admin:video_tasks:list",
|
||||
ttl=min(5, CacheTTL.ADMIN_USAGE_RECORDS),
|
||||
user_specific=True,
|
||||
vary_by=["status", "user_id", "model", "page", "page_size"],
|
||||
)
|
||||
async def handle(self, context: ApiRequestContext) -> Any:
|
||||
db = context.db
|
||||
user = context.user
|
||||
@@ -270,8 +278,8 @@ class VideoTaskListAdapter(DashboardAdapter):
|
||||
escaped = self.model.replace("%", "\\%").replace("_", "\\_")
|
||||
query = query.filter(VideoTask.model.ilike(f"%{escaped}%"))
|
||||
|
||||
# 统计总数
|
||||
total = query.count()
|
||||
# 统计总数(避免 Query.count() 生成大子查询)
|
||||
total = int(query.with_entities(func.count(VideoTask.id)).scalar() or 0)
|
||||
|
||||
# 分页
|
||||
offset = (self.page - 1) * self.page_size
|
||||
@@ -341,6 +349,11 @@ class VideoTaskListAdapter(DashboardAdapter):
|
||||
class VideoTaskStatsAdapter(DashboardAdapter):
|
||||
"""视频任务统计适配器"""
|
||||
|
||||
@cache_result(
|
||||
key_prefix="admin:video_tasks:stats",
|
||||
ttl=min(5, CacheTTL.ADMIN_USAGE_RECORDS),
|
||||
user_specific=True,
|
||||
)
|
||||
async def handle(self, context: ApiRequestContext) -> Any:
|
||||
db = context.db
|
||||
user = context.user
|
||||
@@ -350,8 +363,8 @@ class VideoTaskStatsAdapter(DashboardAdapter):
|
||||
if not is_admin:
|
||||
base_query = base_query.filter(VideoTask.user_id == user.id)
|
||||
|
||||
# 总数
|
||||
total = base_query.count()
|
||||
# 总数(避免 Query.count() 生成大子查询)
|
||||
total = int(base_query.with_entities(func.count(VideoTask.id)).scalar() or 0)
|
||||
|
||||
# 按状态分组
|
||||
status_stats = (
|
||||
@@ -379,7 +392,12 @@ class VideoTaskStatsAdapter(DashboardAdapter):
|
||||
|
||||
# 今日任务数
|
||||
today = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
today_count = base_query.filter(VideoTask.created_at >= today).count()
|
||||
today_count = int(
|
||||
base_query.filter(VideoTask.created_at >= today)
|
||||
.with_entities(func.count(VideoTask.id))
|
||||
.scalar()
|
||||
or 0
|
||||
)
|
||||
|
||||
# 管理员额外统计
|
||||
result = {
|
||||
|
||||
Reference in New Issue
Block a user