From 2b8716cf72b90164b3e4b24937672a5f1810c6da Mon Sep 17 00:00:00 2001 From: yml2213 Date: Mon, 22 Jun 2026 20:40:30 +0800 Subject: [PATCH] =?UTF-8?q?=E7=99=BB=E5=BD=95=E4=BB=BB=E5=8A=A1=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=BB=A3=E7=90=86=E5=88=87=E6=8D=A2=E6=AC=A1=E6=95=B0?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端: LoginBatchRequest 增加 max_proxy_retries 字段,默认10,0=无限 - 核心: DouyuLogin 极验验证循环根据 max_proxy_retries 控制重试 - 前端: 筛选栏增加代理切换次数输入框,Tooltip 提示0=无限切换直到成功 Co-Authored-By: Claude Fable 5 --- core/douyu/login.py | 16 +++++++++++++--- web/backend/routers/login.py | 1 + web/backend/schemas.py | 1 + web/backend/services/login_service.py | 3 +++ web/frontend/src/api/modules.ts | 4 ++-- web/frontend/src/pages/LoginTasksPage.tsx | 18 ++++++++++++++++-- 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/core/douyu/login.py b/core/douyu/login.py index 8453e6e..ced6f5c 100644 --- a/core/douyu/login.py +++ b/core/douyu/login.py @@ -61,11 +61,13 @@ class DouyuLogin: proxy_api_url: Optional[str] = None, timeout: tuple[float, float] = REQUEST_TIMEOUT, max_geetest_retries: int = 5, + max_proxy_retries: int = 10, ): self.account = account self.proxy = proxy self.timeout = timeout self.max_geetest_retries = max_geetest_retries + self.max_proxy_retries = max_proxy_retries # 0=无限切换直到成功 self.session = requests.Session() # 初始化代理管理器 @@ -317,9 +319,15 @@ class DouyuLogin: """ logger.info("开始极验 fullpage 验证...") - for attempt in range(self.max_geetest_retries): + # max_proxy_retries=0 表示无限重试直到成功 + max_attempts = self.max_proxy_retries if self.max_proxy_retries > 0 else 999999 + + for attempt in range(max_attempts): try: - logger.info(f"极验验证尝试 {attempt + 1}/{self.max_geetest_retries}") + if self.max_proxy_retries > 0: + logger.info(f"极验验证尝试 {attempt + 1}/{max_attempts}") + else: + logger.info(f"极验验证尝试 {attempt + 1} (无限重试)") # 按斗鱼登录页 HAR:fullpage 智能检测流程,不进入图片滑块。 str_16 = _generate_seed() @@ -372,7 +380,9 @@ class DouyuLogin: time.sleep(2) continue - raise ValueError(f"极验验证失败,已重试 {self.max_geetest_retries} 次") + if self.max_proxy_retries > 0: + raise ValueError(f"极验验证失败,已重试 {max_attempts} 次") + raise ValueError("极验验证失败(无限重试模式仍未能通过)") def _second_login(self, gt: str, challenge: str, validate: str, seccode: str, code_token: str) -> str: diff --git a/web/backend/routers/login.py b/web/backend/routers/login.py index 7cd298b..703f905 100644 --- a/web/backend/routers/login.py +++ b/web/backend/routers/login.py @@ -58,6 +58,7 @@ async def create_batch( created_by=current.id, creator_role=current.role, max_geetest_retries=req.max_geetest_retries, + max_proxy_retries=req.max_proxy_retries, proxy_config=proxy, log_queue=log_queue, loop=loop, diff --git a/web/backend/schemas.py b/web/backend/schemas.py index f5a27da..5008f5e 100644 --- a/web/backend/schemas.py +++ b/web/backend/schemas.py @@ -125,6 +125,7 @@ class AccountOut(BaseModel): class LoginBatchRequest(BaseModel): account_ids: list[int] max_geetest_retries: int = 5 + max_proxy_retries: int = 10 # 代理切换次数,0=无限切换直到成功 concurrency: int = 3 # 并发数,1-10 diff --git a/web/backend/services/login_service.py b/web/backend/services/login_service.py index d8776dd..37f6076 100644 --- a/web/backend/services/login_service.py +++ b/web/backend/services/login_service.py @@ -26,6 +26,7 @@ class LoginBatchRunner: created_by: int, creator_role: str, max_geetest_retries: int = 5, + max_proxy_retries: int = 10, proxy_config: Optional[ProxyConfigModel] = None, log_queue: Optional[asyncio.Queue] = None, loop: Optional[asyncio.AbstractEventLoop] = None, @@ -36,6 +37,7 @@ class LoginBatchRunner: self.created_by = created_by self.creator_role = creator_role self.max_geetest_retries = max_geetest_retries + self.max_proxy_retries = max_proxy_retries self.proxy_config = proxy_config self.log_queue = log_queue self.loop = loop @@ -126,6 +128,7 @@ class LoginBatchRunner: account, proxy=proxy_dict, max_geetest_retries=self.max_geetest_retries, + max_proxy_retries=self.max_proxy_retries, ) result = loginer.login() diff --git a/web/frontend/src/api/modules.ts b/web/frontend/src/api/modules.ts index 2dd57c4..73c3560 100644 --- a/web/frontend/src/api/modules.ts +++ b/web/frontend/src/api/modules.ts @@ -56,8 +56,8 @@ export const accountApi = { }; export const loginApi = { - createBatch: (account_ids: number[], max_geetest_retries?: number, concurrency?: number) => - api.post('/login/batch', { account_ids, max_geetest_retries, concurrency }), + createBatch: (account_ids: number[], max_geetest_retries?: number, concurrency?: number, max_proxy_retries?: number) => + api.post('/login/batch', { account_ids, max_geetest_retries, concurrency, max_proxy_retries }), listTasks: (batch_id?: string) => api.get('/login/tasks', { params: batch_id ? { batch_id } : {} }), stop: (batch_id: string) => api.post(`/login/stop/${batch_id}`), diff --git a/web/frontend/src/pages/LoginTasksPage.tsx b/web/frontend/src/pages/LoginTasksPage.tsx index 6ff314a..3d81ca7 100644 --- a/web/frontend/src/pages/LoginTasksPage.tsx +++ b/web/frontend/src/pages/LoginTasksPage.tsx @@ -2,7 +2,7 @@ import { useEffect, useState, useRef, useMemo, useCallback } from 'react'; import { Table, Button, Select, message, Tag, Space, Spin, InputNumber, Tooltip, Popconfirm, theme, } from 'antd'; -import { PlayCircleOutlined, StopOutlined, FilterOutlined, ThunderboltOutlined, ReloadOutlined, DownOutlined, UpOutlined, DeleteOutlined } from '@ant-design/icons'; +import { PlayCircleOutlined, StopOutlined, FilterOutlined, ThunderboltOutlined, ReloadOutlined, DownOutlined, UpOutlined, DeleteOutlined, SwapOutlined } from '@ant-design/icons'; import { accountApi, loginApi } from '../api/modules'; import { getUser, hasPerm } from '../store/auth'; import { formatTime } from '../utils/time'; @@ -33,6 +33,7 @@ export default function LoginTasksPage() { const [wsConnected, setWsConnected] = useState(false); const [selectedTags, setSelectedTags] = useState([]); const [concurrency, setConcurrency] = useState(3); + const [maxProxyRetries, setMaxProxyRetries] = useState(10); const [logVisible, setLogVisible] = useState(true); const [selectedRowKeys, setSelectedRowKeys] = useState([]); const wsRef = useRef(null); @@ -134,7 +135,7 @@ export default function LoginTasksPage() { setLoading(true); setLogs([]); try { - const result = await loginApi.createBatch(accountIds, 5, concurrency); + const result = await loginApi.createBatch(accountIds, 5, concurrency, maxProxyRetries); setBatchId(result.batch_id); message.success(`已创建登录任务,共 ${result.count} 个账号`); @@ -346,6 +347,19 @@ export default function LoginTasksPage() { /> + + + + setMaxProxyRetries(v ?? 10)} + style={{ width: 55 }} + size="small" + /> + +