feat(douyu): 和平小店扭蛋碎片查询 + 深色模式修复 + 任务状态 WS 即时推送

- 碎片: 新增 query_xpd_fragments 任务, 复用 e语言 gamecoin 接口取扭蛋币 jb2,
  账号已存角色回退(规避 Livelink 风控), accounts.xpd_fragments 字段与迁移
- 列表: 和平小店移除鱼翅/限制兑换/换绑时间列, 新增扭蛋碎片列;
  操作区硬编码白底改用 theme token, 深色模式适配(三个手册共用)
- 轮询: 任务状态经 WS(level=task)即时推送(二维码/绑定进度即时展示),
  pending/running/终态全覆盖, REST/WS 载荷统一 douyu_task_payload,
  raw 快照递归剥离, HTTP 轮询降频 5s/30s 兜底
This commit is contained in:
yml2213
2026-08-06 20:21:28 +08:00
parent c05c0e044e
commit a515ea6e0a
10 changed files with 394 additions and 178 deletions
+84 -3
View File
@@ -22,6 +22,7 @@ from .douyu_service import (
douyu_config_value,
ensure_douyu_config,
latest_success_cookie,
douyu_task_payload,
update_account_profile_from_cookie,
)
@@ -345,6 +346,22 @@ class DouyuBatchRunner:
return ""
return f"{role_name}{channel}" if channel else role_name
def _push_task_event(self, task: DouyuTask) -> None:
"""向批次 WS 推送任务状态事件(level=task),前端即时更新不依赖轮询。"""
if not self.log_queue or not self.loop:
return
try:
payload = douyu_task_payload(task)
except Exception:
logger.exception("[douyu] 推送任务状态失败: task_id={}", task.id)
return
event = {
"level": "task",
"message": "",
"task": payload,
}
asyncio.run_coroutine_threadsafe(self.log_queue.put(event), self.loop)
def _mark_task(
self,
db: Session,
@@ -359,6 +376,7 @@ class DouyuBatchRunner:
task.result = result
task.finished_at = datetime.now(timezone.utc)
db.commit()
self._push_task_event(task)
def _update_task_progress(
self,
@@ -373,6 +391,7 @@ class DouyuBatchRunner:
if result is not None:
task.result = result
db.commit()
self._push_task_event(task)
def _upsert_goods(self, db: Session, goods: list[dict]) -> None:
now = datetime.now(timezone.utc)
@@ -1334,6 +1353,67 @@ class DouyuBatchRunner:
{"balance": balance, "role": role, "area_id": area_id},
)
def _execute_query_xpd_fragments(
self,
db: Session,
task: DouyuTask,
account: Account,
cookie: str,
config: dict,
):
"""查询和平小店扭蛋碎片数量。
优先现查角色;getrole 受限(Livelink 风控/失效)时回退账号已存角色,
保证已绑定账号仍可查询。
"""
client = self._client(cookie)
act_id = str(config["xpd_act_id"])
embed_query: dict = {}
openid = str(account.xpd_openid or "")
roleid = str(account.xpd_role_id or "")
plat = str(account.xpd_plat_id or "1")
areaid = str(account.xpd_area_id or 1)
role: dict = {}
try:
ctx = self._xpd_role_context(client, config)
embed_query = ctx["embed"]["query"]
role = ctx["role"] if isinstance(ctx.get("role"), dict) else {}
if role.get("role_id"):
role_area = self._xpd_area_id(role, account)
openid = str(role.get("game_open_id") or "") or openid
roleid = str(role.get("role_id") or "") or roleid
plat = str(role.get("plat_id") or "1") or plat
areaid = str(role_area) or areaid
self._apply_xpd_role_to_account(account, role, role_area)
except Exception:
pass
if not openid or not roleid:
self._mark_task(db, task, "failed", "未获取到小店绑定角色,请先生成二维码扫码绑定")
return
result = client.xpd_fragments(
embed_query=embed_query,
act_id=act_id,
openid=openid,
roleid=roleid,
plat=plat,
areaid=areaid,
)
fragments = result.get("fragments")
account.xpd_fragments = fragments
account.xpd_bind_status = "xpd_fragments_queried"
account.updated_at = datetime.now(timezone.utc)
db.commit()
if fragments is None:
self._mark_task(db, task, "failed", "未获取到小店扭蛋碎片数量")
return
self._mark_task(
db,
task,
"success",
f"小店扭蛋碎片: {fragments}",
{"fragments": fragments, "role": role, "area_id": int(areaid)},
)
def _execute_get_bind_qr(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
client = self._client(cookie)
qr_act_alias = self._bind_qr_act_alias(config)
@@ -2584,9 +2664,7 @@ class DouyuBatchRunner:
if not task or self._stop.is_set():
return
account = task.account
task.status = "running"
task.message = "执行中"
worker_db.commit()
self._update_task_progress(worker_db, task, "running", "执行中")
with self._counter_lock:
self._started += 1
@@ -2631,6 +2709,7 @@ class DouyuBatchRunner:
"query_xpd_role": self._execute_query_xpd_role,
"refresh_xpd_goods": self._execute_refresh_xpd_goods,
"query_xpd_balance": self._execute_query_xpd_balance,
"query_xpd_fragments": self._execute_query_xpd_fragments,
}.get(task.task_type)
if handler is None:
self._mark_task(worker_db, task, "failed", "不支持的任务类型")
@@ -2669,6 +2748,8 @@ class DouyuBatchRunner:
task.status = "pending"
task.message = "等待执行"
self.db.commit()
for task in tasks:
self._push_task_event(task)
total = len(tasks)
with ThreadPoolExecutor(max_workers=self.concurrency) as executor: