From accb76b9da603fb8f0311d8c70af45fabb055aa5 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Mon, 22 Jun 2026 19:34:13 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=99=BB=E5=BD=95=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E9=A1=B5=E9=9D=A2=E5=8A=A0=E8=BD=BD=E6=85=A2=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端: list_tasks 用 IN 批量查 Account 替代逐条查询,消除 N+1 - 后端: list_accounts 用 joinedload 预加载 assigned_user,消除懒加载 - 前端: 首次加载 accounts 和 tasks 并行请求 Co-Authored-By: Claude Fable 5 --- web/backend/routers/accounts.py | 3 ++- web/backend/routers/login.py | 20 ++++++++++++-------- web/frontend/src/pages/LoginTasksPage.tsx | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/web/backend/routers/accounts.py b/web/backend/routers/accounts.py index d62ec7a..6e04d8e 100644 --- a/web/backend/routers/accounts.py +++ b/web/backend/routers/accounts.py @@ -11,6 +11,7 @@ from ..schemas import AccountImport, AccountAssign, AccountTag, AccountOut, Batc from ..deps import get_current_user, require_permission from ..permissions import has_permission from sqlalchemy import func +from sqlalchemy.orm import joinedload router = APIRouter(prefix="/api/accounts", tags=["账号管理"]) @@ -64,7 +65,7 @@ def list_accounts( if tag: query = query.filter(Account.tag == tag) - accounts = query.order_by(Account.id).all() + accounts = query.order_by(Account.id).options(joinedload(Account.assigned_user)).all() result = [] for acc in accounts: item = AccountOut( diff --git a/web/backend/routers/login.py b/web/backend/routers/login.py index 9df433d..7cd298b 100644 --- a/web/backend/routers/login.py +++ b/web/backend/routers/login.py @@ -87,24 +87,28 @@ def list_tasks( current: User = Depends(get_current_user), ): """查看登录任务列表。""" - query = db.query(LoginTask) + query = db.query(LoginTask).join(Account, LoginTask.account_id == Account.id) # 客服只能看自己账号的任务 if not has_permission(current.role, "login:view_all"): - query = query.join(Account, LoginTask.account_id == Account.id).filter( - Account.assigned_to == current.id - ) + query = query.filter(Account.assigned_to == current.id) if batch_id: query = query.filter(LoginTask.batch_id == batch_id) - tasks = query.order_by(LoginTask.id.desc()).limit(200).all() + rows = query.order_by(LoginTask.id.desc()).limit(200).all() + # 批量收集 account_id,一次性查出 username + account_ids = [t.account_id for t in rows] + accounts_map = {} + if account_ids: + accs = db.query(Account).filter(Account.id.in_(account_ids)).all() + accounts_map = {a.id: a.username for a in accs} + result = [] - for t in tasks: - acc = db.query(Account).filter(Account.id == t.account_id).first() + for t in rows: result.append(LoginTaskOut( id=t.id, batch_id=t.batch_id, account_id=t.account_id, - account_username=acc.username if acc else "", + account_username=accounts_map.get(t.account_id, ""), status=t.status, cookie=t.cookie or "", message=t.message or "", created_by=t.created_by, created_at=t.created_at, finished_at=t.finished_at, )) diff --git a/web/frontend/src/pages/LoginTasksPage.tsx b/web/frontend/src/pages/LoginTasksPage.tsx index eb80f46..3fd46ac 100644 --- a/web/frontend/src/pages/LoginTasksPage.tsx +++ b/web/frontend/src/pages/LoginTasksPage.tsx @@ -108,7 +108,7 @@ export default function LoginTasksPage() { }; useEffect(() => { - loadAccounts(); + Promise.all([loadAccounts(), loadTasks()]); }, []); // 日志自动滚动到底部