feat(account-check): 登录接口可选 wgapi / iframe

账号检测原来把 WgapiLoginAPI 写死在执行器里,收不到邮箱验证码时只能干等;
登录任务早就可选 iframe(邮箱验证走 iframe 接口)。默认仍 wgapi,旧行为不变。
This commit is contained in:
yml2213
2026-10-06 20:19:26 +08:00
parent 39388c56b5
commit 5da12df16b
8 changed files with 279 additions and 17 deletions
+1
View File
@@ -113,6 +113,7 @@ def create_account_check_batch(
max_total_time=req.max_total_time,
use_proxy=req.use_proxy,
proxy_config=proxy_config,
api_strategy=req.api_strategy,
)
thread = threading.Thread(target=runner.run, daemon=True)
thread.start()
+4
View File
@@ -331,6 +331,9 @@ class AccountCheckBatchRequest(BaseModel):
max_login_retries: int = Field(0, ge=0, le=50)
max_total_time: float = Field(300, ge=0, le=3600)
use_proxy: bool = False
# 登录接口策略,与登录任务同一套取值:wgapi(默认,保持原行为)或 iframe。
# 有些人只有其中一套能收到邮箱验证码,所以要能选。
api_strategy: str = "wgapi"
class AccountCheckItemOut(BaseModel):
@@ -353,6 +356,7 @@ class AccountCheckBatchOut(BaseModel):
max_login_retries: int
max_total_time: float
use_proxy: bool
api_strategy: str = "wgapi"
total: int
finished_count: int
running_count: int
@@ -14,7 +14,7 @@ from typing import cast
from loguru import logger
from core.douyu import DouyuLogin, WgapiLoginAPI
from core.douyu import DouyuLogin, create_login_api_strategy
from core.douyu.login import AccountLike
from core.douyu.proxy_fetcher import ProxyFetcher
@@ -109,6 +109,9 @@ class AccountCheckBatch:
max_total_time: float
use_proxy: bool
items: list[AccountCheckItemState]
#: 检测使用的登录接口策略(wgapi / iframe)—— 与登录任务同一套取值。
#: 默认 wgapi,保持账号检测原来的行为不变。
api_strategy: str = "wgapi"
status: str = "pending"
message: str = "等待开始"
created_at: datetime = field(default_factory=_now)
@@ -347,7 +350,7 @@ class AccountCheckRunner:
max_total_time=self.batch.max_total_time,
proxy_fetcher=self._shared_proxy_fetcher,
stop_event=self._stop,
api_strategy=WgapiLoginAPI(),
api_strategy=create_login_api_strategy(self.batch.api_strategy),
).check_account()
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._set_item(
@@ -395,6 +398,7 @@ class AccountCheckRunner:
"max_login_retries": self.batch.max_login_retries,
"max_total_time": self.batch.max_total_time,
"use_proxy": self.batch.use_proxy,
"api_strategy": self.batch.api_strategy,
"total": len(self.batch.items),
"finished_count": finished_count,
"running_count": running_count,
@@ -486,6 +490,7 @@ class AccountCheckRegistry:
max_total_time: float,
use_proxy: bool = False,
proxy_config: ProxyConfigModel | None = None,
api_strategy: str = "wgapi",
) -> AccountCheckRunner:
batch_id = uuid.uuid4().hex[:12]
normalized_retries = max_login_retries if max_login_retries > 0 else 20
@@ -496,6 +501,7 @@ class AccountCheckRegistry:
max_login_retries=normalized_retries,
max_total_time=max(0.0, float(max_total_time or 0)),
use_proxy=bool(use_proxy),
api_strategy=str(api_strategy or "wgapi"),
items=[
AccountCheckItemState(
line=account.line,
+2 -10
View File
@@ -14,7 +14,7 @@ from loguru import logger
from sqlalchemy import and_, func, or_
from sqlalchemy.orm import Session
from core.douyu import DouyuLogin, IframeLoginAPI, WgapiLoginAPI
from core.douyu import DouyuLogin, create_login_api_strategy
from core.douyu.login import AccountLike
from core.douyu.proxy_fetcher import ProxyFetcher
@@ -26,14 +26,6 @@ from .account_lifecycle import on_ck_verdict, on_login_success
from .cookie_check_service import check_douyu_cookie
from .douyu_service import LOGIN_ATTEMPT_IN_FLIGHT_STATUSES
def _create_api_strategy(strategy_name: str):
"""根据名称创建登录接口策略实例。"""
if strategy_name == "iframe":
return IframeLoginAPI()
return WgapiLoginAPI()
CHECK_STATUS_MESSAGES = {
"account_cancelled": "账号已注销",
"password_wrong": "账号密码错误",
@@ -223,7 +215,7 @@ class LoginBatchRunner:
self.loop = loop
self.batch_id = uuid.uuid4().hex[:12]
self.concurrency = max(1, min(concurrency, 10)) # 限制 1-10
self.api_strategy = _create_api_strategy(api_strategy)
self.api_strategy = create_login_api_strategy(api_strategy)
self.mode = mode if mode in ("login", "check", "relogin") else "login"
self._stop = threading.Event()
self._counter_lock = threading.Lock()
+3
View File
@@ -415,6 +415,8 @@ export interface AccountCheckBatchRequest {
max_login_retries?: number;
max_total_time?: number;
use_proxy?: boolean;
/** 登录接口:wgapi(默认,保持原行为)或 iframe;与登录任务同一套取值 */
api_strategy?: string;
}
export interface AccountCheckItem {
@@ -438,6 +440,7 @@ export interface AccountCheckBatch {
max_login_retries: number;
max_total_time: number;
use_proxy: boolean;
api_strategy: string;
total: number;
finished_count: number;
running_count: number;
+33 -5
View File
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
Button, Radio, Select, Card, Col, Input, InputNumber, Popconfirm, Row, Space, Statistic, Switch, Table, Tag, Typography,
Button, Radio, Select, Card, Col, Input, InputNumber, Popconfirm, Row, Space, Statistic, Switch, Table, Tag, Tooltip, Typography,
} from 'antd';
import { message } from '../utils/antdMessage';
import type { TableProps } from 'antd';
@@ -102,6 +102,9 @@ export default function AccountCheckPage() {
const [maxTotalTime, setMaxTotalTime] = useState(() => readStoredNumber('account_check_max_total_time', 300));
const [maxLoginRetries, setMaxLoginRetries] = useState(() => readStoredNumber('account_check_max_login_retries', 0));
const [useProxy, setUseProxy] = useState(() => localStorage.getItem('account_check_use_proxy') === 'true');
// 登录接口:与登录任务同款选择。默认 wgapi —— 账号检测原来就是写死 wgapi,
// 保持不变;收不到邮箱验证码时可以换成 iframe(旧版,邮箱验证走 iframe 接口)。
const [apiStrategy, setApiStrategy] = useState(() => localStorage.getItem('account_check_api_strategy') || 'wgapi');
const navigate = useNavigate();
const [batch, setBatch] = useState<AccountCheckBatch | null>(null);
const [starting, setStarting] = useState(false);
@@ -160,6 +163,10 @@ export default function AccountCheckPage() {
localStorage.setItem('account_check_use_proxy', String(useProxy));
}, [useProxy]);
useEffect(() => {
localStorage.setItem('account_check_api_strategy', apiStrategy);
}, [apiStrategy]);
useEffect(() => {
if (restoredBatchRef.current) return;
restoredBatchRef.current = true;
@@ -247,6 +254,7 @@ export default function AccountCheckPage() {
max_total_time: maxTotalTime,
max_login_retries: maxLoginRetries,
use_proxy: useProxy,
api_strategy: apiStrategy,
});
setBatch(data);
localStorage.setItem(BATCH_STORAGE_KEY, data.batch_id);
@@ -440,7 +448,22 @@ export default function AccountCheckPage() {
</Space>
)}
<Row gutter={[12, 12]} align="bottom">
<Col xs={8} md={4}>
<Col xs={12} md={5}>
<Tooltip title="登录使用的接口版本。wgapi 为新版(默认,与原来一致);iframe 为旧版备选,邮箱验证码走 iframe 接口。收不到验证码时可以换一个试。">
<Text type="secondary">登录接口</Text>
</Tooltip>
<Select
value={apiStrategy}
onChange={setApiStrategy}
disabled={isRunning}
style={{ width: '100%' }}
options={[
{ value: 'wgapi', label: 'wgapi(默认)' },
{ value: 'iframe', label: 'iframe(旧版备选)' },
]}
/>
</Col>
<Col xs={8} md={3}>
<Text type="secondary">并发</Text>
<InputNumber
min={1}
@@ -480,7 +503,7 @@ export default function AccountCheckPage() {
<Button disabled>次</Button>
</Space.Compact>
</Col>
<Col xs={12} md={4}>
<Col xs={12} md={3}>
<Text type="secondary">代理</Text>
<div style={{ height: 32, display: 'flex', alignItems: 'center' }}>
<Switch
@@ -492,7 +515,7 @@ export default function AccountCheckPage() {
/>
</div>
</Col>
<Col xs={24} md={8}>
<Col xs={24} md={5}>
<Space wrap>
<Button
type="primary"
@@ -528,7 +551,12 @@ export default function AccountCheckPage() {
</Card>
<Card
title="检测结果"
title={(
<Space size={8}>
检测结果
{batch && <Tag color="blue">接口: {batch.api_strategy || 'wgapi'}</Tag>}
</Space>
)}
extra={(
<Space>
<Button icon={<ReloadOutlined />} loading={refreshing} disabled={!batchId} onClick={refreshBatch}>