Files
live-hub-py/web/backend/services/login_service.py
T
2026-06-22 13:11:15 +08:00

140 lines
4.9 KiB
Python

"""登录服务:复用 douyu/ 核心模块,在线程池中执行登录并推送日志。"""
import asyncio
import threading
import time
import uuid
from datetime import datetime
from typing import Optional
from sqlalchemy.orm import Session
from douyu import DouyuLogin
from douyu.config import Account, ProxyConfig as DouyuProxyConfig
from ..models import Account as AccountModel, LoginTask, ProxyConfig as ProxyConfigModel
from ..permissions import has_permission
class LoginBatchRunner:
"""批量登录执行器,在线程中运行,通过 asyncio.Queue 推送日志。"""
def __init__(
self,
db: Session,
account_ids: list[int],
created_by: int,
creator_role: str,
max_geetest_retries: int = 5,
proxy_config: Optional[ProxyConfigModel] = None,
log_queue: Optional[asyncio.Queue] = None,
loop: Optional[asyncio.AbstractEventLoop] = None,
):
self.db = db
self.account_ids = account_ids
self.created_by = created_by
self.creator_role = creator_role
self.max_geetest_retries = max_geetest_retries
self.proxy_config = proxy_config
self.log_queue = log_queue
self.loop = loop
self.batch_id = uuid.uuid4().hex[:12]
self._stop = threading.Event()
def stop(self):
self._stop.set()
def _push_log(self, level: str, message: str):
if self.log_queue and self.loop:
asyncio.run_coroutine_threadsafe(
self.log_queue.put({"level": level, "message": message}),
self.loop,
)
def run(self):
"""在线程中执行批量登录。"""
batch_id = self.batch_id
self._push_log("info", f"批量登录任务 {batch_id} 开始,共 {len(self.account_ids)} 个账号")
# 创建任务记录
tasks = []
for aid in self.account_ids:
acc = self.db.query(AccountModel).filter(AccountModel.id == aid).first()
if not acc:
continue
# 权限检查:客服只能跑分配给自己的
if not has_permission(self.creator_role, "login:view_all"):
if acc.assigned_to != self.created_by:
self._push_log("warning", f"跳过无权账号: {acc.username}")
continue
task = LoginTask(
batch_id=batch_id,
account_id=aid,
status="pending",
created_by=self.created_by,
)
self.db.add(task)
tasks.append((task, acc))
self.db.commit()
# 构建代理配置
proxy_url = None
proxy_api_url = None
if self.proxy_config and self.proxy_config.enabled:
if self.proxy_config.http or self.proxy_config.https:
proxy_url = {
'http': self.proxy_config.http or self.proxy_config.https,
'https': self.proxy_config.https or self.proxy_config.http,
}
elif self.proxy_config.api_url:
proxy_api_url = self.proxy_config.api_url
for i, (task, acc) in enumerate(tasks):
if self._stop.is_set():
self._push_log("warning", "任务已停止")
break
task.status = "running"
self.db.commit()
self._push_log("info", f"[{i+1}/{len(tasks)}] 开始登录: {acc.username}")
try:
account = Account(
username=acc.username,
password=acc.password,
email=acc.email,
email_password=acc.email_password,
email_imap_server=acc.email_imap_server or "",
email_imap_port=acc.email_imap_port or 993,
)
loginer = DouyuLogin(
account,
proxy=proxy_url,
proxy_api_url=proxy_api_url,
max_geetest_retries=self.max_geetest_retries,
)
result = loginer.login()
if result.success:
task.status = "success"
task.cookie = result.cookie
task.message = "登录成功"
self._push_log("success", f"[{i+1}] {acc.username} 登录成功")
else:
task.status = "failed"
task.message = result.message
self._push_log("error", f"[{i+1}] {acc.username} 登录失败: {result.message}")
except Exception as e:
task.status = "error"
task.message = str(e)
self._push_log("error", f"[{i+1}] {acc.username} 登录异常: {e}")
task.finished_at = datetime.utcnow()
self.db.commit()
self._push_log("info", f"批量登录任务 {batch_id} 完成")