refactor: 补全services层+消除core/models.py冗余+修复network.py缩进错误

高优先级问题1 - Router层业务逻辑泄漏:
- 新建 services/proxy_service.py: 从routers/proxy.py提取ProxyService类,
  封装代理/白名单测试逻辑、全局状态管理、所有core/ import
- 新建 services/account_service.py: 从routers/accounts.py提取
  split_account_line、cookie_account_ids_query、parse_and_build_accounts,
  封装core.douyu.email_verifier的import
- 重写routers/proxy.py为薄HTTP/WS层,移除所有core/直接依赖
- 精简routers/accounts.py,从account_service导入业务函数
- 将_active_batches全局状态移入login_service.py的BatchRegistry类
- routers/login.py改为使用batch_registry单例

高优先级问题2 - 两套models.py冗余:
- 删除core/models.py(Account dataclass + ProxyConfig死代码)
- 在core/douyu/login.py新增AccountLike Protocol替代Account dataclass
- login_service.py中Account(...)构造改为SimpleNamespace(...)
- 清理core/__init__.py导出

附带修复:
- core/geetest/common/network.py的get_picture()函数缩进错误
  (缺少if match:块和data赋值语句)
This commit is contained in:
yml2213
2026-06-23 07:34:17 +08:00
parent 12c31c2e09
commit 2ab6724543
10 changed files with 411 additions and 333 deletions
-4
View File
@@ -1,5 +1 @@
"""核心业务逻辑"""
from .models import Account, ProxyConfig
__all__ = ["Account", "ProxyConfig"]
+13 -3
View File
@@ -4,11 +4,10 @@ import re
import json
import time
import requests
from typing import Mapping, Optional, Tuple
from typing import Mapping, Optional, Protocol, Tuple
from urllib.parse import urlsplit, urlunsplit
from loguru import logger
from core.models import Account
from .crypto import encrypt_password, encrypt_nickname_or_phone
from .email_verifier import EmailVerifier
from .proxy import ProxyManager, get_proxy_manager
@@ -25,6 +24,17 @@ from core.geetest.common.network import (
from utils.http_logger import log_http
class AccountLike(Protocol):
"""DouyuLogin 所需的最小账号接口,ORM 对象或 SimpleNamespace 均可满足。"""
username: str
password: str
email: str
email_password: str
email_imap_server: str
email_imap_port: int
email_imap_ssl: bool
class LoginResult:
"""登录结果"""
@@ -57,7 +67,7 @@ class DouyuLogin:
def __init__(
self,
account: Account,
account: AccountLike,
proxy: Optional[str | Mapping[str, str]] = None,
proxy_api_url: Optional[str] = None,
timeout: tuple[float, float] = REQUEST_TIMEOUT,
+4
View File
@@ -266,6 +266,10 @@ def get_picture(gt:str, challenge:str) -> tuple[str, str, list[int], str, str, s
response.raise_for_status()
logger.debug("极验 get.php 原始响应: {}", response.text[:500])
match = re.search(r'\((.*)\)$', response.text)
if match:
json_str = match.group(1)
data = json.loads(json_str)
if 'data' in data and isinstance(data['data'], dict):
# 新版极验格式
inner_data = data['data']
-28
View File
@@ -1,28 +0,0 @@
"""核心数据模型"""
from dataclasses import dataclass
@dataclass
class Account:
"""账号配置"""
username: str
password: str
email: str
email_password: str
email_imap_server: str
email_imap_port: int = 993
email_imap_ssl: bool = True
@dataclass
class ProxyConfig:
"""代理配置"""
enabled: bool = False
api_url: str = ""
http: str = ""
https: str = ""
# 白名单配置
whitelist_enabled: bool = False
whitelist_uid: str = ""
whitelist_ukey: str = ""