style: 统一 Ruff 代码格式

This commit is contained in:
yml2213
2026-08-30 21:04:52 +08:00
parent c891ac982e
commit 47e19ed7b2
90 changed files with 5574 additions and 2350 deletions
+29 -23
View File
@@ -8,27 +8,31 @@ from sqlalchemy.orm import Session
from ..models import Account, LoginTask
EMAIL_PATTERN = re.compile(r'^[^\s@|]+@[^\s@|]+\.[^\s@|]+$')
EMAIL_PATTERN = re.compile(r"^[^\s@|]+@[^\s@|]+\.[^\s@|]+$")
def split_account_line(line: str) -> list[str]:
"""拆分一行账号文本,支持 |、tab、逗号、空格分隔。"""
if '|' in line:
return line.split('|')
if '\t' in line:
return line.split('\t')
if ',' in line:
if "|" in line:
return line.split("|")
if "\t" in line:
return line.split("\t")
if "," in line:
return next(csv.reader([line]))
return line.split()
def cookie_account_ids_query(db: Session):
"""返回有成功登录记录(cookie非空)的账号ID子查询。"""
return db.query(LoginTask.account_id).filter(
LoginTask.status == 'success',
LoginTask.cookie != '',
LoginTask.cookie.isnot(None),
).distinct()
return (
db.query(LoginTask.account_id)
.filter(
LoginTask.status == "success",
LoginTask.cookie != "",
LoginTask.cookie.isnot(None),
)
.distinct()
)
def parse_and_build_accounts(
@@ -61,9 +65,9 @@ def parse_and_build_accounts(
skipped = 0
duplicated = 0
seen_in_batch: set[str] = set()
for line in text.strip().split('\n'):
for line in text.strip().split("\n"):
line = line.strip()
if not line or line.startswith('#'):
if not line or line.startswith("#"):
continue
parts = split_account_line(line)
if len(parts) < 4:
@@ -88,14 +92,16 @@ def parse_and_build_accounts(
seen_in_batch.add(username_key)
email_cfg = get_email_config_for_account(email)
accounts.append(Account(
username=username,
password=password,
email=email,
email_password=email_password,
email_imap_server=email_cfg['server'],
email_imap_port=email_cfg.get('port', 993),
email_imap_ssl=email_cfg.get('ssl', True),
tag=tag,
))
accounts.append(
Account(
username=username,
password=password,
email=email,
email_password=email_password,
email_imap_server=email_cfg["server"],
email_imap_port=email_cfg.get("port", 993),
email_imap_ssl=email_cfg.get("ssl", True),
tag=tag,
)
)
return accounts, skipped, duplicated
+14 -3
View File
@@ -12,8 +12,17 @@ from ..models import AuditLog, User
_SENSITIVE_KEY_PARTS = (
"cookie", "token", "password", "passwd", "secret", "signature", "sign",
"qr_data", "authorization", "credential", "private_key",
"cookie",
"token",
"password",
"passwd",
"secret",
"signature",
"sign",
"qr_data",
"authorization",
"credential",
"private_key",
)
@@ -42,7 +51,9 @@ def record_audit(
) -> AuditLog:
"""加入一条审计记录;调用方负责与业务变更一起提交事务。"""
if isinstance(detail, Mapping):
detail_text = json.dumps(_safe_value(detail), ensure_ascii=False, separators=(",", ":"))
detail_text = json.dumps(
_safe_value(detail), ensure_ascii=False, separators=(",", ":")
)
elif detail is None:
detail_text = ""
else:
+124 -57
View File
@@ -200,7 +200,9 @@ def snapshot_from_batch(batch: HuyaRegisterBatch) -> dict:
}
def load_batch_snapshot(batch_id: str, *, recover_interrupted: bool = True) -> dict | None:
def load_batch_snapshot(
batch_id: str, *, recover_interrupted: bool = True
) -> dict | None:
"""从数据库加载批次详情;若服务中断则标记为 interrupted。"""
db = SessionLocal()
try:
@@ -246,7 +248,9 @@ def load_batch_snapshot(batch_id: str, *, recover_interrupted: bool = True) -> d
db.close()
def list_batch_summaries(limit: int = 50, live_batch_ids: set[str] | None = None) -> list[dict]:
def list_batch_summaries(
limit: int = 50, live_batch_ids: set[str] | None = None
) -> list[dict]:
"""列出最近的注册批次摘要。live_batch_ids 中的 running 保持运行中。"""
live = live_batch_ids or set()
db = SessionLocal()
@@ -269,42 +273,51 @@ def list_batch_summaries(limit: int = 50, live_batch_ids: set[str] | None = None
if status == "running":
running_count = max(
0,
int(row.total or 0) - int(row.success_count or 0) - int(row.failed_count or 0) - int(row.stopped_count or 0),
int(row.total or 0)
- int(row.success_count or 0)
- int(row.failed_count or 0)
- int(row.stopped_count or 0),
)
result.append({
"batch_id": row.batch_id,
"status": status,
"message": message,
"tag": row.tag or "",
"created_by": row.created_by,
"concurrency": int(row.concurrency or 1),
"wait_seconds": float(row.wait_seconds or 180),
"poll_interval": float(row.poll_interval or 5),
"password_prefix": row.password_prefix or "hy",
"use_proxy": bool(row.use_proxy),
"total": int(row.total or 0),
"success_count": int(row.success_count or 0),
"failed_count": int(row.failed_count or 0),
"stopped_count": int(row.stopped_count or 0),
"running_count": running_count,
"created_at": row.created_at,
"started_at": row.started_at,
"finished_at": row.finished_at,
"items": [],
})
result.append(
{
"batch_id": row.batch_id,
"status": status,
"message": message,
"tag": row.tag or "",
"created_by": row.created_by,
"concurrency": int(row.concurrency or 1),
"wait_seconds": float(row.wait_seconds or 180),
"poll_interval": float(row.poll_interval or 5),
"password_prefix": row.password_prefix or "hy",
"use_proxy": bool(row.use_proxy),
"total": int(row.total or 0),
"success_count": int(row.success_count or 0),
"failed_count": int(row.failed_count or 0),
"stopped_count": int(row.stopped_count or 0),
"running_count": running_count,
"created_at": row.created_at,
"started_at": row.started_at,
"finished_at": row.finished_at,
"items": [],
}
)
return result
finally:
db.close()
def _refresh_batch_counts(batch_row: HuyaRegisterBatchModel, items: list[HuyaRegisterItemModel]):
def _refresh_batch_counts(
batch_row: HuyaRegisterBatchModel, items: list[HuyaRegisterItemModel]
):
batch_row.total = len(items)
batch_row.success_count = sum(1 for item in items if item.status == "success")
batch_row.failed_count = sum(1 for item in items if item.status == "error")
batch_row.stopped_count = sum(1 for item in items if item.status == "stopped")
def format_success_export_line(username: str, uid: str, password: str, phone: str, sms_url: str) -> str:
def format_success_export_line(
username: str, uid: str, password: str, phone: str, sms_url: str
) -> str:
"""统一成功导出格式。"""
account = (username or uid or "").strip()
return f"{account}----{password or ''}----{phone or ''}----{sms_url or ''}"
@@ -319,7 +332,9 @@ def export_success_logs_text(
"""从成功流水表导出 txt。"""
db = SessionLocal()
try:
query = db.query(HuyaRegisterSuccessLog).order_by(HuyaRegisterSuccessLog.id.asc())
query = db.query(HuyaRegisterSuccessLog).order_by(
HuyaRegisterSuccessLog.id.asc()
)
if batch_id:
query = query.filter(HuyaRegisterSuccessLog.batch_id == batch_id)
if tag:
@@ -350,7 +365,9 @@ def list_success_logs(
"""列出成功流水(含密码,供管理端展示/导出)。"""
db = SessionLocal()
try:
query = db.query(HuyaRegisterSuccessLog).order_by(HuyaRegisterSuccessLog.id.desc())
query = db.query(HuyaRegisterSuccessLog).order_by(
HuyaRegisterSuccessLog.id.desc()
)
if batch_id:
query = query.filter(HuyaRegisterSuccessLog.batch_id == batch_id)
if tag:
@@ -406,7 +423,11 @@ class HuyaRegisterRunner:
def _create_proxy_fetcher(self) -> ProxyFetcher | None:
"""按需创建 API 代理获取器。"""
if not self.batch.use_proxy or not self.proxy_config or not self.proxy_config.enabled:
if (
not self.batch.use_proxy
or not self.proxy_config
or not self.proxy_config.enabled
):
return None
if not self.proxy_config.api_url:
return None
@@ -414,9 +435,15 @@ class HuyaRegisterRunner:
wl_platform = "xiequ"
wl_credentials = None
if self.proxy_config.whitelist_enabled:
wl_platform = getattr(self.proxy_config, "whitelist_platform", None) or "xiequ"
wl_platform = (
getattr(self.proxy_config, "whitelist_platform", None) or "xiequ"
)
wl_credentials = getattr(self.proxy_config, "whitelist_credentials", None)
if not wl_credentials and self.proxy_config.whitelist_uid and self.proxy_config.whitelist_ukey:
if (
not wl_credentials
and self.proxy_config.whitelist_uid
and self.proxy_config.whitelist_ukey
):
wl_credentials = {
"uid": self.proxy_config.whitelist_uid,
"ukey": self.proxy_config.whitelist_ukey,
@@ -446,7 +473,11 @@ class HuyaRegisterRunner:
return
db = SessionLocal()
try:
row = db.query(HuyaRegisterBatchModel).filter(HuyaRegisterBatchModel.id == self.batch.db_id).first()
row = (
db.query(HuyaRegisterBatchModel)
.filter(HuyaRegisterBatchModel.id == self.batch.db_id)
.first()
)
if not row:
return
row.status = self.batch.status
@@ -485,7 +516,11 @@ class HuyaRegisterRunner:
return
db = SessionLocal()
try:
row = db.query(HuyaRegisterItemModel).filter(HuyaRegisterItemModel.id == item.db_id).first()
row = (
db.query(HuyaRegisterItemModel)
.filter(HuyaRegisterItemModel.id == item.db_id)
.first()
)
if not row:
return
row.status = item.status
@@ -518,12 +553,16 @@ class HuyaRegisterRunner:
setattr(item, key, value)
self._persist_item(index)
def _save_success(self, index: int, result: HuyaAutoRegisterResult) -> tuple[int | None, str, str]:
def _save_success(
self, index: int, result: HuyaAutoRegisterResult
) -> tuple[int | None, str, str]:
"""成功时:写账号 + 成功流水(成功一个写一条,立即可导出)。"""
db = SessionLocal()
try:
# upsert_huya_cookie 内部会 commit 一次
account = upsert_huya_cookie(db, result.cookie, tag=self.batch.tag, username_hint="")
account = upsert_huya_cookie(
db, result.cookie, tag=self.batch.tag, username_hint=""
)
account.game_phone = result.phone or account.game_phone or ""
if result.username:
account.username = result.username
@@ -537,23 +576,29 @@ class HuyaRegisterRunner:
account.updated_at = _now()
item = self.batch.items[index]
db.add(HuyaRegisterSuccessLog(
batch_id=self.batch.batch_id,
item_id=item.db_id,
account_id=account.id,
phone=result.phone or item.phone,
username=result.username or account.username or "",
uid=result.uid or account.uid or account.yyuid or "",
password=result.password or "",
sms_url=sms_url,
tag=self.batch.tag,
provider=result.provider or item.provider,
created_by=self.batch.created_by,
created_at=_now(),
))
db.add(
HuyaRegisterSuccessLog(
batch_id=self.batch.batch_id,
item_id=item.db_id,
account_id=account.id,
phone=result.phone or item.phone,
username=result.username or account.username or "",
uid=result.uid or account.uid or account.yyuid or "",
password=result.password or "",
sms_url=sms_url,
tag=self.batch.tag,
provider=result.provider or item.provider,
created_by=self.batch.created_by,
created_at=_now(),
)
)
db.commit()
db.refresh(account)
return account.id, account.username or "", account.uid or account.yyuid or ""
return (
account.id,
account.username or "",
account.uid or account.yyuid or "",
)
finally:
db.close()
@@ -578,12 +623,16 @@ class HuyaRegisterRunner:
def _run_one(self, index: int, item: SmsLine):
if self._stop.is_set():
self._set_item(index, status="stopped", message="已停止", finished_at=_now())
self._set_item(
index, status="stopped", message="已停止", finished_at=_now()
)
return
proxies, proxy_error = self._resolve_proxy()
if proxy_error:
self._set_item(index, status="error", message=proxy_error, finished_at=_now())
self._set_item(
index, status="error", message=proxy_error, finished_at=_now()
)
return
self._set_item(
@@ -714,7 +763,12 @@ class HuyaRegisterRunner:
futures = []
for index in indices:
if self._stop.is_set():
self._set_item(index, status="stopped", message="已停止", finished_at=_now())
self._set_item(
index,
status="stopped",
message="已停止",
finished_at=_now(),
)
continue
item = self.sms_lines[index]
futures.append(executor.submit(self._run_one, index, item))
@@ -841,7 +895,11 @@ class HuyaRegisterRegistry:
# DB 同步为 running,避免返回 pending 导致前端误判
db = SessionLocal()
try:
row = db.query(HuyaRegisterBatchModel).filter(HuyaRegisterBatchModel.id == db_id).first()
row = (
db.query(HuyaRegisterBatchModel)
.filter(HuyaRegisterBatchModel.id == db_id)
.first()
)
if row:
row.status = "running"
row.message = "批次运行中"
@@ -850,7 +908,9 @@ class HuyaRegisterRegistry:
finally:
db.close()
runner = HuyaRegisterRunner(batch=batch, sms_lines=sms_lines, proxy_config=proxy_config)
runner = HuyaRegisterRunner(
batch=batch, sms_lines=sms_lines, proxy_config=proxy_config
)
with self._lock:
self._runners[batch_id] = runner
return runner
@@ -930,7 +990,9 @@ class HuyaRegisterRegistry:
if poll_interval is not None:
batch_row.poll_interval = int(max(1.0, float(poll_interval)))
if password_prefix is not None:
batch_row.password_prefix = (password_prefix or "hy").strip()[:8] or "hy"
batch_row.password_prefix = (password_prefix or "hy").strip()[
:8
] or "hy"
if fixed_password is not None:
batch_row.fixed_password = (fixed_password or "").strip()
if use_proxy is not None:
@@ -965,7 +1027,12 @@ class HuyaRegisterRegistry:
batch = _batch_from_db(batch_row, item_rows)
sms_lines = [
SmsLine(phone=item.phone, url=item.sms_url, provider=item.provider, raw=f"{item.phone}----{item.sms_url}")
SmsLine(
phone=item.phone,
url=item.sms_url,
provider=item.provider,
raw=f"{item.phone}----{item.sms_url}",
)
for item in batch.items
]
finally:
+64 -31
View File
@@ -19,15 +19,24 @@ from .huya_runner_recharge import RechargeMixin
class HuyaBatchRunner(
HuyaBatchRunnerCore, BindMixin, GoodsMixin, RechargeMixin,
HuyaBatchRunnerCore,
BindMixin,
GoodsMixin,
RechargeMixin,
):
"""批量执行虎牙任务(功能域 Mixin 聚合 + 批次调度)。"""
def _execute_one(self, task_id: int, account_info: dict, config_info: dict, total: int):
def _execute_one(
self, task_id: int, account_info: dict, config_info: dict, total: int
):
worker_db = SessionLocal()
try:
task = worker_db.query(HuyaTask).filter(HuyaTask.id == task_id).first()
account = worker_db.query(HuyaAccount).filter(HuyaAccount.id == account_info["account_id"]).first()
account = (
worker_db.query(HuyaAccount)
.filter(HuyaAccount.id == account_info["account_id"])
.first()
)
if not task or not account:
return
@@ -59,28 +68,48 @@ class HuyaBatchRunner(
"create_recharge_order",
}:
self._mark_task(worker_db, task, "failed", "该虎牙任务执行器暂未实现")
self._push_log("warning", f"[{current}] {name} 暂未实现: {self.task_type}")
self._push_log(
"warning", f"[{current}] {name} 暂未实现: {self.task_type}"
)
return
try:
if self.task_type == "query_points":
self._execute_query_points(worker_db, task, account, account_info, config_info)
self._execute_query_points(
worker_db, task, account, account_info, config_info
)
elif self.task_type == "refresh_goods":
self._execute_refresh_goods(worker_db, task, account, account_info, config_info)
self._execute_refresh_goods(
worker_db, task, account, account_info, config_info
)
elif self.task_type == "refresh_recharge_goods":
self._execute_refresh_recharge_goods(worker_db, task, account, account_info, config_info)
self._execute_refresh_recharge_goods(
worker_db, task, account, account_info, config_info
)
elif self.task_type == "exchange_goods":
self._execute_exchange_goods(worker_db, task, account, account_info, config_info)
self._execute_exchange_goods(
worker_db, task, account, account_info, config_info
)
elif self.task_type == "create_recharge_order":
self._execute_create_recharge_order(worker_db, task, account, account_info, config_info)
self._execute_create_recharge_order(
worker_db, task, account, account_info, config_info
)
elif self.task_type == "get_bind_qr":
self._execute_get_bind_qr(worker_db, task, account, account_info, config_info)
self._execute_get_bind_qr(
worker_db, task, account, account_info, config_info
)
elif self.task_type == "confirm_bind":
self._execute_confirm_bind(worker_db, task, account, account_info, config_info)
self._execute_confirm_bind(
worker_db, task, account, account_info, config_info
)
elif self.task_type == "query_game_name":
self._execute_query_game_name(worker_db, task, account, account_info, config_info)
self._execute_query_game_name(
worker_db, task, account, account_info, config_info
)
elif self.task_type == "query_exchange_records":
self._execute_query_exchange_records(worker_db, task, account, account_info, config_info)
self._execute_query_exchange_records(
worker_db, task, account, account_info, config_info
)
worker_db.refresh(task)
if task.status == "success":
self._push_log("success", f"[{current}] {name} {task.message}")
@@ -124,17 +153,19 @@ class HuyaBatchRunner(
task.status = "pending"
task.message = "等待执行"
task.finished_at = None
task_infos.append({
"task_id": task.id,
"account_info": {
"account_id": account.id,
"uid": account.uid or "",
"yyuid": account.yyuid or "",
"username": account.username or "",
"nickname": account.nickname or "",
"cookie": normalize_huya_cookie(account.cookie or ""),
},
})
task_infos.append(
{
"task_id": task.id,
"account_info": {
"account_id": account.id,
"uid": account.uid or "",
"yyuid": account.yyuid or "",
"username": account.username or "",
"nickname": account.nickname or "",
"cookie": normalize_huya_cookie(account.cookie or ""),
},
}
)
self.db.commit()
total = len(task_infos)
@@ -149,13 +180,15 @@ class HuyaBatchRunner(
if self._stop.is_set():
self._push_log("warning", "任务已停止,跳过剩余账号")
break
futures.append(executor.submit(
self._execute_one,
item["task_id"],
item["account_info"],
config_info,
total,
))
futures.append(
executor.submit(
self._execute_one,
item["task_id"],
item["account_info"],
config_info,
total,
)
)
for future in as_completed(futures):
try:
+4 -2
View File
@@ -20,8 +20,10 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .huya_runner import HuyaBatchRunner
class HuyaBatchRunnerCore:
"""虎牙任务执行器公共基础:批次状态、日志、任务落库。"""
def __init__(
self,
db: Session,
@@ -156,7 +158,8 @@ class HuyaBatchRegistry:
expired = [
batch_id
for batch_id, batch in self._batches.items()
if batch.get("finished") and now - float(batch.get("finished_at") or now) > ttl_seconds
if batch.get("finished")
and now - float(batch.get("finished_at") or now) > ttl_seconds
]
for batch_id in expired:
self._batches.pop(batch_id, None)
@@ -206,4 +209,3 @@ class HuyaBatchRegistry:
huya_batch_registry = HuyaBatchRegistry()
+35 -16
View File
@@ -37,7 +37,10 @@ def apply_huya_config_defaults(config: HuyaConfig) -> bool:
"""补齐虎牙配置默认值,返回是否发生变更。"""
changed = False
for field in HUYA_CONFIG_FIELDS:
if field == "bind_act_id" and str(getattr(config, field, "") or "").strip() == "17096":
if (
field == "bind_act_id"
and str(getattr(config, field, "") or "").strip() == "17096"
):
setattr(config, field, HUYA_CONFIG_DEFAULTS[field])
changed = True
continue
@@ -160,7 +163,9 @@ def split_huya_password_line(line: str) -> HuyaPasswordLine | None:
)
def import_huya_password_accounts(db: Session, text: str, tag: str = "") -> tuple[int, int]:
def import_huya_password_accounts(
db: Session, text: str, tag: str = ""
) -> tuple[int, int]:
"""导入虎牙账号密码,返回 (导入/更新数, 跳过数)。"""
created_or_updated = 0
skipped = 0
@@ -207,13 +212,17 @@ def import_huya_password_accounts(db: Session, text: str, tag: str = "") -> tupl
return created_or_updated, skipped
def _upsert_huya_account(db: Session, parsed: dict, tag: str = "", status: str | None = None) -> HuyaAccount:
def _upsert_huya_account(
db: Session, parsed: dict, tag: str = "", status: str | None = None
) -> HuyaAccount:
"""按 uid/yyuid 新增或更新虎牙账号。"""
account = None
if parsed["uid"]:
account = db.query(HuyaAccount).filter(HuyaAccount.uid == parsed["uid"]).first()
if account is None and parsed["yyuid"]:
account = db.query(HuyaAccount).filter(HuyaAccount.yyuid == parsed["yyuid"]).first()
account = (
db.query(HuyaAccount).filter(HuyaAccount.yyuid == parsed["yyuid"]).first()
)
if account is None:
account = HuyaAccount(
@@ -273,13 +282,17 @@ def save_huya_login_cookie_to_account(
return account
def upsert_huya_cookie(db: Session, cookie: str, tag: str = "", username_hint: str = "") -> HuyaAccount:
def upsert_huya_cookie(
db: Session, cookie: str, tag: str = "", username_hint: str = ""
) -> HuyaAccount:
"""保存单条登录得到的虎牙 Cookie。"""
line = f"{username_hint}----{cookie}" if username_hint else cookie
parsed = parse_huya_cookie_line(line)
if not parsed:
raise ValueError("登录成功但 Cookie 中没有识别到虎牙 uid")
account = _upsert_huya_account(db, parsed, tag=(tag or "").strip(), status="login_success")
account = _upsert_huya_account(
db, parsed, tag=(tag or "").strip(), status="login_success"
)
db.commit()
db.refresh(account)
return account
@@ -368,18 +381,24 @@ def create_planned_tasks(
batch_id = uuid.uuid4().hex[:12]
payload = payload or {}
accounts = db.query(HuyaAccount).filter(HuyaAccount.id.in_(account_ids)).all()
if task_type in {"refresh_goods", "refresh_recharge_goods", "create_recharge_order"} and accounts:
if (
task_type
in {"refresh_goods", "refresh_recharge_goods", "create_recharge_order"}
and accounts
):
# 全局快照和单笔支付二维码使用一个选中的 CK 即可;兑换商品需要保留多账号批量任务。
accounts = accounts[:1]
for account in accounts:
db.add(HuyaTask(
batch_id=batch_id,
account_id=account.id,
task_type=task_type,
status="planned",
message="任务已创建,等待执行",
result={"payload": payload} if payload else None,
created_by=created_by,
))
db.add(
HuyaTask(
batch_id=batch_id,
account_id=account.id,
task_type=task_type,
status="planned",
message="任务已创建,等待执行",
result={"payload": payload} if payload else None,
created_by=created_by,
)
)
db.commit()
return batch_id, len(accounts)
+30 -15
View File
@@ -8,7 +8,11 @@ from typing import Optional
from sqlalchemy.orm import Session
from core.douyu.proxy import resolve_working_proxy, verify_proxy_url, parse_proxy_response
from core.douyu.proxy import (
resolve_working_proxy,
verify_proxy_url,
parse_proxy_response,
)
from core.douyu.proxy_platforms import create_adapter, get_platform_labels
from core.douyu.proxy_platforms.base import _get_local_exit_ip
from ..models import ProxyConfig as ProxyConfigModel, AuditLog
@@ -20,8 +24,8 @@ def _build_whitelist_params(cfg: ProxyConfigModel) -> dict:
Returns:
{"whitelist_platform": str, "whitelist_credentials": dict|None}
"""
platform = getattr(cfg, 'whitelist_platform', None) or "xiequ"
credentials = getattr(cfg, 'whitelist_credentials', None)
platform = getattr(cfg, "whitelist_platform", None) or "xiequ"
credentials = getattr(cfg, "whitelist_credentials", None)
# 向后兼容:旧字段有值但新字段为空时,自动迁移
if not credentials and cfg.whitelist_uid and cfg.whitelist_ukey:
@@ -57,7 +61,10 @@ class ProxyService:
# 应用层自动迁移:旧字段有值但新字段为空时,填充新字段
if cfg.whitelist_uid and cfg.whitelist_ukey and not cfg.whitelist_credentials:
cfg.whitelist_platform = "xiequ"
cfg.whitelist_credentials = {"uid": cfg.whitelist_uid, "ukey": cfg.whitelist_ukey}
cfg.whitelist_credentials = {
"uid": cfg.whitelist_uid,
"ukey": cfg.whitelist_ukey,
}
db.commit()
return cfg
@@ -65,7 +72,10 @@ class ProxyService:
@staticmethod
def update_config(
db: Session,
enabled, api_url, http, https,
enabled,
api_url,
http,
https,
whitelist_enabled,
whitelist_platform="xiequ",
whitelist_credentials=None,
@@ -96,12 +106,14 @@ class ProxyService:
db.refresh(cfg)
if current_user:
db.add(AuditLog(
user_id=current_user.id,
username=current_user.username,
action="proxy:update",
target="proxy_config",
))
db.add(
AuditLog(
user_id=current_user.id,
username=current_user.username,
action="proxy:update",
target="proxy_config",
)
)
db.commit()
return cfg
@@ -258,18 +270,21 @@ class ProxyService:
# 3. 检查并同步白名单
records = adapter.get_whitelist()
in_list = any(r.get('ip') == local_ip for r in records)
in_list = any(r.get("ip") == local_ip for r in records)
if records:
push("info", f"白名单共 {len(records)} 条记录")
if in_list:
record = next((r for r in records if r.get('ip') == local_ip), {})
memo = record.get('memo', '')
record = next((r for r in records if r.get("ip") == local_ip), {})
memo = record.get("memo", "")
if memo == adapter.memo:
push("success", f"本机IP {local_ip} 已在白名单中 (备注正确)")
else:
push("warning", f'本机IP {local_ip} 备注不匹配 (当前: "{memo}"),更新中...')
push(
"warning",
f'本机IP {local_ip} 备注不匹配 (当前: "{memo}"),更新中...',
)
sync_ok, sync_msg = adapter.sync_ip(local_ip)
push("success" if sync_ok else "error", f"白名单更新: {sync_msg}")
else:
+61 -19
View File
@@ -32,9 +32,22 @@ def _as_utc(value: datetime | None) -> datetime | None:
def cleanup_orphan_yyb_tasks(db: Session, message: str) -> int:
rows = db.query(YybRechargeTask).filter(
YybRechargeTask.status.in_(["created", "waiting_login", "ready", "running", "ordering", "waiting_payment"])
).all()
rows = (
db.query(YybRechargeTask)
.filter(
YybRechargeTask.status.in_(
[
"created",
"waiting_login",
"ready",
"running",
"ordering",
"waiting_payment",
]
)
)
.all()
)
for task in rows:
if task.status == "ordering":
task.status = "waiting_payment"
@@ -50,7 +63,9 @@ def cleanup_orphan_yyb_tasks(db: Session, message: str) -> int:
return len(rows)
def sync_task(db: Session, task: YybRechargeTask, worker: YybWorkerClient) -> YybRechargeTask:
def sync_task(
db: Session, task: YybRechargeTask, worker: YybWorkerClient
) -> YybRechargeTask:
data = worker.get_job(task.worker_job_id)
task.status = str(data.get("status", task.status))
task.phase = str(data.get("phase", task.phase))
@@ -59,10 +74,16 @@ def sync_task(db: Session, task: YybRechargeTask, worker: YybWorkerClient) -> Yy
task.provider = str(data["provider"])
if data.get("qr_data"):
task.login_qr_data = str(data["qr_data"])
task.result = {**(task.result or {}), "login_qr_mime_type": data.get("qr_mime_type", "image/jpeg")}
task.result = {
**(task.result or {}),
"login_qr_mime_type": data.get("qr_mime_type", "image/jpeg"),
}
if data.get("payment_qr_data"):
task.payment_qr_data = str(data["payment_qr_data"])
task.result = {**(task.result or {}), "payment_qr_mime_type": data.get("payment_qr_mime_type", "image/png")}
task.result = {
**(task.result or {}),
"payment_qr_mime_type": data.get("payment_qr_mime_type", "image/png"),
}
task.result = {
**(task.result or {}),
"logs": data.get("logs", []),
@@ -76,32 +97,53 @@ def sync_task(db: Session, task: YybRechargeTask, worker: YybWorkerClient) -> Yy
task.payment_last_checked_at = last_checked_at
if task.status in {"success", "failed"} and task.finished_at is None:
task.finished_at = _utcnow()
if task.status not in {"success", "failed", "stopped"} and task.finished_at is not None:
if (
task.status not in {"success", "failed", "stopped"}
and task.finished_at is not None
):
task.finished_at = None
db.commit()
db.refresh(task)
return task
def public_task(task: YybRechargeTask, include_qr: bool = True,
include_payment_qr: bool | None = None,
creator_username: str = "") -> dict[str, Any]:
def public_task(
task: YybRechargeTask,
include_qr: bool = True,
include_payment_qr: bool | None = None,
creator_username: str = "",
) -> dict[str, Any]:
result: dict[str, Any] = {
"id": task.id, "task_id": task.task_id,
"provider": task.provider, "platform": task.platform, "points": task.points,
"id": task.id,
"task_id": task.task_id,
"provider": task.provider,
"platform": task.platform,
"points": task.points,
"price_fen": task.price_fen,
"product_id": task.product_id, "zone_id": task.zone_id, "zone_name": task.zone_name,
"role_id": task.role_id, "role_name": task.role_name, "status": task.status,
"phase": task.phase, "message": task.message, "result": task.result,
"created_by": task.created_by, "created_by_username": creator_username,
"created_at": _as_utc(task.created_at), "finished_at": _as_utc(task.finished_at),
"product_id": task.product_id,
"zone_id": task.zone_id,
"zone_name": task.zone_name,
"role_id": task.role_id,
"role_name": task.role_name,
"status": task.status,
"phase": task.phase,
"message": task.message,
"result": task.result,
"created_by": task.created_by,
"created_by_username": creator_username,
"created_at": _as_utc(task.created_at),
"finished_at": _as_utc(task.finished_at),
"payment_started_at": _as_utc(task.payment_started_at),
"payment_qr_created_at": _as_utc(task.payment_qr_created_at),
"payment_last_checked_at": _as_utc(task.payment_last_checked_at),
}
if task.result:
result["login_qr_mime_type"] = task.result.get("login_qr_mime_type", "image/jpeg")
result["payment_qr_mime_type"] = task.result.get("payment_qr_mime_type", "image/png")
result["login_qr_mime_type"] = task.result.get(
"login_qr_mime_type", "image/jpeg"
)
result["payment_qr_mime_type"] = task.result.get(
"payment_qr_mime_type", "image/png"
)
if include_payment_qr is None:
include_payment_qr = include_qr
if include_qr:
+33 -11
View File
@@ -18,13 +18,20 @@ class YybWorkerClient:
self.key = os.getenv("YYB_WORKER_KEY", "")
self.timeout = float(os.getenv("YYB_WORKER_TIMEOUT", "30"))
def _request(self, method: str, path: str, payload: dict[str, Any] | None = None) -> dict[str, Any]:
def _request(
self, method: str, path: str, payload: dict[str, Any] | None = None
) -> dict[str, Any]:
headers = {"Accept": "application/json"}
if self.key:
headers["Authorization"] = f"Bearer {self.key}"
try:
response = requests.request(method, self.base_url + path, json=payload,
headers=headers, timeout=self.timeout)
response = requests.request(
method,
self.base_url + path,
json=payload,
headers=headers,
timeout=self.timeout,
)
data = response.json()
except (requests.RequestException, ValueError) as exc:
raise YybWorkerError(f"应用宝 Worker 不可用: {exc}") from exc
@@ -35,19 +42,34 @@ class YybWorkerClient:
def create_job(self) -> dict[str, Any]:
return self._request("POST", "/v1/jobs")
def login(self, worker_job_id: str, provider: str, timeout: int = 600) -> dict[str, Any]:
return self._request("POST", f"/v1/jobs/{worker_job_id}/login",
{"provider": provider, "timeout": timeout})
def login(
self, worker_job_id: str, provider: str, timeout: int = 600
) -> dict[str, Any]:
return self._request(
"POST",
f"/v1/jobs/{worker_job_id}/login",
{"provider": provider, "timeout": timeout},
)
def get_job(self, worker_job_id: str) -> dict[str, Any]:
return self._request("GET", f"/v1/jobs/{worker_job_id}")
def selection_options(self, worker_job_id: str, platform: str,
points: int | None = None, zone_id: str | None = None) -> dict[str, Any]:
return self._request("POST", f"/v1/jobs/{worker_job_id}/selection-options",
{"platform": platform, "points": points, "zone_id": zone_id})
def selection_options(
self,
worker_job_id: str,
platform: str,
points: int | None = None,
zone_id: str | None = None,
) -> dict[str, Any]:
return self._request(
"POST",
f"/v1/jobs/{worker_job_id}/selection-options",
{"platform": platform, "points": points, "zone_id": zone_id},
)
def selection(self, worker_job_id: str, selection: dict[str, Any]) -> dict[str, Any]:
def selection(
self, worker_job_id: str, selection: dict[str, Any]
) -> dict[str, Any]:
return self._request("POST", f"/v1/jobs/{worker_job_id}/selection", selection)
def payment(self, worker_job_id: str) -> dict[str, Any]: