优化斗鱼绑定状态同步
This commit is contained in:
@@ -120,7 +120,7 @@ class DouyuConfig(Base):
|
|||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
manual_id = Column(String(64), default="G4KA4Qnz4LDp7")
|
manual_id = Column(String(64), default="G4KA4Qnz4LDp7")
|
||||||
rid = Column(String(64), default="9263298")
|
rid = Column(String(64), default="9263298")
|
||||||
bind_act_alias = Column(String(64), default="20250213NQCYX")
|
bind_act_alias = Column(String(64), default="20260120QYOOB")
|
||||||
confirm_act_alias = Column(String(64), default="20260120QYOOB")
|
confirm_act_alias = Column(String(64), default="20260120QYOOB")
|
||||||
legacy_act_alias = Column(String(64), default="cjm")
|
legacy_act_alias = Column(String(64), default="cjm")
|
||||||
room_id = Column(String(64), default="9263298")
|
room_id = Column(String(64), default="9263298")
|
||||||
|
|||||||
@@ -542,7 +542,7 @@ class HuyaRechargeGoodsOut(BaseModel):
|
|||||||
class DouyuConfigOut(BaseModel):
|
class DouyuConfigOut(BaseModel):
|
||||||
manual_id: str = "G4KA4Qnz4LDp7"
|
manual_id: str = "G4KA4Qnz4LDp7"
|
||||||
rid: str = "9263298"
|
rid: str = "9263298"
|
||||||
bind_act_alias: str = "20250213NQCYX"
|
bind_act_alias: str = "20260120QYOOB"
|
||||||
confirm_act_alias: str = "20260120QYOOB"
|
confirm_act_alias: str = "20260120QYOOB"
|
||||||
legacy_act_alias: str = "cjm"
|
legacy_act_alias: str = "cjm"
|
||||||
room_id: str = "9263298"
|
room_id: str = "9263298"
|
||||||
|
|||||||
@@ -150,6 +150,16 @@ class DouyuBatchRunner:
|
|||||||
def _current_bind_act_alias(cls, config: dict) -> str:
|
def _current_bind_act_alias(cls, config: dict) -> str:
|
||||||
return cls._confirm_act_alias(config) or cls._bind_qr_act_alias(config)
|
return cls._confirm_act_alias(config) or cls._bind_qr_act_alias(config)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _action_act_aliases(cls, config: dict) -> list[str]:
|
||||||
|
"""当前活动动作 alias;不包含只用于查询最新扫码态的 legacy/cjm。"""
|
||||||
|
aliases: list[str] = []
|
||||||
|
for key in ("confirm_act_alias", "bind_act_alias"):
|
||||||
|
alias = cls._action_act_alias(config, key)
|
||||||
|
if alias and alias not in aliases:
|
||||||
|
aliases.append(alias)
|
||||||
|
return aliases
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _role_channel(bind_info: dict) -> str:
|
def _role_channel(bind_info: dict) -> str:
|
||||||
return " / ".join(
|
return " / ".join(
|
||||||
@@ -194,8 +204,7 @@ class DouyuBatchRunner:
|
|||||||
if not bind_info:
|
if not bind_info:
|
||||||
return False
|
return False
|
||||||
role_name = str(bind_info.get("role_name") or "").strip()
|
role_name = str(bind_info.get("role_name") or "").strip()
|
||||||
is_bound = cls._is_bound_act(bind_info) or bool(role_name)
|
if not role_name or not cls._is_bound_act(bind_info):
|
||||||
if not is_bound:
|
|
||||||
return False
|
return False
|
||||||
return not cls._can_change_role(bind_info)
|
return not cls._can_change_role(bind_info)
|
||||||
|
|
||||||
@@ -768,6 +777,32 @@ class DouyuBatchRunner:
|
|||||||
return info
|
return info
|
||||||
return ordered[0]
|
return ordered[0]
|
||||||
|
|
||||||
|
def _pick_current_bound_info(
|
||||||
|
self,
|
||||||
|
candidates: list[dict],
|
||||||
|
config: dict,
|
||||||
|
*,
|
||||||
|
extra_prefer_aliases: list[str] | None = None,
|
||||||
|
) -> dict | None:
|
||||||
|
"""选当前活动已生效绑定,避免把 legacy/cjm 的待确认态当成当前角色。"""
|
||||||
|
if not candidates:
|
||||||
|
return None
|
||||||
|
prefer = []
|
||||||
|
for alias in [*(extra_prefer_aliases or []), *self._action_act_aliases(config)]:
|
||||||
|
alias = str(alias or "").strip()
|
||||||
|
if alias and alias not in prefer:
|
||||||
|
prefer.append(alias)
|
||||||
|
|
||||||
|
bound = [
|
||||||
|
info
|
||||||
|
for info in candidates
|
||||||
|
if self._is_bound_act(info) and str(info.get("role_name") or "").strip()
|
||||||
|
and str(info.get("act_alias") or "") in prefer
|
||||||
|
]
|
||||||
|
if not bound:
|
||||||
|
return None
|
||||||
|
return sorted(bound, key=lambda info: prefer.index(str(info.get("act_alias") or "")))[0]
|
||||||
|
|
||||||
def _pick_baseline_bind_info(
|
def _pick_baseline_bind_info(
|
||||||
self,
|
self,
|
||||||
candidates: list[dict],
|
candidates: list[dict],
|
||||||
@@ -781,26 +816,7 @@ class DouyuBatchRunner:
|
|||||||
"""
|
"""
|
||||||
if not candidates:
|
if not candidates:
|
||||||
return None
|
return None
|
||||||
prefer = []
|
return self._pick_current_bound_info(candidates, config)
|
||||||
for key in ("confirm_act_alias", "bind_act_alias", "legacy_act_alias"):
|
|
||||||
alias = str(config.get(key) or "").strip()
|
|
||||||
if alias and alias not in prefer:
|
|
||||||
prefer.append(alias)
|
|
||||||
|
|
||||||
# 先找“已绑定且有角色”的活动结果
|
|
||||||
for alias in prefer:
|
|
||||||
for info in candidates:
|
|
||||||
if str(info.get("act_alias") or "") != alias:
|
|
||||||
continue
|
|
||||||
if self._is_bound_act(info) and str(info.get("role_name") or "").strip():
|
|
||||||
return info
|
|
||||||
|
|
||||||
# 再退回任意有角色的结果(仍按活动 alias 优先)
|
|
||||||
return self._pick_bind_info(
|
|
||||||
candidates,
|
|
||||||
prefer_pending=False,
|
|
||||||
prefer_aliases=prefer,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _wait_bind_role_result(
|
def _wait_bind_role_result(
|
||||||
self,
|
self,
|
||||||
@@ -992,19 +1008,21 @@ class DouyuBatchRunner:
|
|||||||
self._mark_task(db, task, "failed", "查询绑定信息失败,请检查 Cookie 或 actAlias")
|
self._mark_task(db, task, "failed", "查询绑定信息失败,请检查 Cookie 或 actAlias")
|
||||||
return
|
return
|
||||||
# baseline 用活动 alias 的“当前已绑定”;pending 检测优先 cjm 的换绑最新态
|
# baseline 用活动 alias 的“当前已绑定”;pending 检测优先 cjm 的换绑最新态
|
||||||
before = self._pick_baseline_bind_info(before_candidates, config) or before_candidates[0]
|
before = self._pick_baseline_bind_info(before_candidates, config) or {}
|
||||||
# 换绑冷却必须看活动当前绑定(QYOOB),不要用 cjm
|
# 换绑冷却必须看活动当前绑定(QYOOB),不要用 cjm
|
||||||
cooldown_info = self._pick_change_wait_bind_info(before_candidates, config) or before
|
cooldown_info = self._pick_change_wait_bind_info(before_candidates, config)
|
||||||
pending_before = self._pick_bind_info(
|
pending_before = self._pick_bind_info(
|
||||||
before_candidates,
|
before_candidates,
|
||||||
baseline_role_name=str(before.get("role_name") or ""),
|
baseline_role_name=str(before.get("role_name") or ""),
|
||||||
baseline_is_bound_act=self._is_bound_act(before),
|
baseline_is_bound_act=self._is_bound_act(before),
|
||||||
prefer_pending=True,
|
prefer_pending=True,
|
||||||
prefer_aliases=query_aliases,
|
prefer_aliases=query_aliases,
|
||||||
) or before
|
) or before or before_candidates[0]
|
||||||
before_snapshot = self._bind_snapshot(before)
|
before_snapshot = self._bind_snapshot(before)
|
||||||
cooldown_snapshot = self._bind_snapshot(cooldown_info)
|
cooldown_snapshot = self._bind_snapshot(cooldown_info)
|
||||||
current_role_name = before_snapshot["role_name"] or cooldown_snapshot["role_name"]
|
current_role_name = before_snapshot["role_name"] or (
|
||||||
|
cooldown_snapshot["role_name"] if cooldown_snapshot["is_bound_act"] else ""
|
||||||
|
)
|
||||||
wait_time = cooldown_snapshot["change_role_wait_time"]
|
wait_time = cooldown_snapshot["change_role_wait_time"]
|
||||||
self._push_log(
|
self._push_log(
|
||||||
"info",
|
"info",
|
||||||
@@ -1118,6 +1136,9 @@ class DouyuBatchRunner:
|
|||||||
query_aliases = self._query_bind_act_aliases(config)
|
query_aliases = self._query_bind_act_aliases(config)
|
||||||
if confirm_alias and confirm_alias not in query_aliases:
|
if confirm_alias and confirm_alias not in query_aliases:
|
||||||
query_aliases = [confirm_alias, *query_aliases]
|
query_aliases = [confirm_alias, *query_aliases]
|
||||||
|
if not confirm_alias:
|
||||||
|
self._mark_task(db, task, "failed", "请先配置确认绑定活动 actAlias")
|
||||||
|
return
|
||||||
if not query_aliases:
|
if not query_aliases:
|
||||||
self._mark_task(db, task, "failed", "请先配置绑定活动 actAlias")
|
self._mark_task(db, task, "failed", "请先配置绑定活动 actAlias")
|
||||||
return
|
return
|
||||||
@@ -1137,13 +1158,10 @@ class DouyuBatchRunner:
|
|||||||
role_name = before_snapshot["role_name"]
|
role_name = before_snapshot["role_name"]
|
||||||
query_alias = str(before.get("act_alias") or "")
|
query_alias = str(before.get("act_alias") or "")
|
||||||
# 确认前“已生效绑定”角色(bound_act=1),确认失败/回查失败时写库用,避免待确认角色污染 game_name
|
# 确认前“已生效绑定”角色(bound_act=1),确认失败/回查失败时写库用,避免待确认角色污染 game_name
|
||||||
before_bound = next(
|
before_bound = self._pick_current_bound_info(
|
||||||
(
|
before_candidates,
|
||||||
info
|
config,
|
||||||
for info in before_candidates
|
extra_prefer_aliases=[confirm_alias],
|
||||||
if self._is_bound_act(info) and str(info.get("role_name") or "").strip()
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
self._push_log(
|
self._push_log(
|
||||||
"info",
|
"info",
|
||||||
@@ -1169,8 +1187,13 @@ class DouyuBatchRunner:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if before_snapshot["is_bound_act"]:
|
before_is_current_bound = (
|
||||||
self._apply_bind_info_to_account(account, before, "bind_confirmed")
|
before_bound is not None
|
||||||
|
and str(before.get("act_alias") or "") == str(before_bound.get("act_alias") or "")
|
||||||
|
and role_name == str(before_bound.get("role_name") or "")
|
||||||
|
)
|
||||||
|
if before_is_current_bound:
|
||||||
|
self._apply_bind_info_to_account(account, before_bound, "bind_confirmed")
|
||||||
self._mark_task(
|
self._mark_task(
|
||||||
db,
|
db,
|
||||||
task,
|
task,
|
||||||
@@ -1180,8 +1203,8 @@ class DouyuBatchRunner:
|
|||||||
"act_alias": confirm_alias or query_alias,
|
"act_alias": confirm_alias or query_alias,
|
||||||
"query_act_alias": query_alias,
|
"query_act_alias": query_alias,
|
||||||
"query_act_aliases": query_aliases,
|
"query_act_aliases": query_aliases,
|
||||||
"before_bind_info": before,
|
"before_bind_info": before_bound,
|
||||||
**before_snapshot,
|
**self._bind_snapshot(before_bound),
|
||||||
"bind_ready_for_confirm": True,
|
"bind_ready_for_confirm": True,
|
||||||
"bind_confirmed": True,
|
"bind_confirmed": True,
|
||||||
"bind_phase": "confirmed",
|
"bind_phase": "confirmed",
|
||||||
@@ -1197,7 +1220,11 @@ class DouyuBatchRunner:
|
|||||||
confirm_msg = str(exc)
|
confirm_msg = str(exc)
|
||||||
self._push_log("warning", f"确认绑定接口失败: {confirm_msg}")
|
self._push_log("warning", f"确认绑定接口失败: {confirm_msg}")
|
||||||
# 待绑定游戏账号侧换绑限制(未到换绑时间等)导致确认失败:保留原绑定并给出明确提示
|
# 待绑定游戏账号侧换绑限制(未到换绑时间等)导致确认失败:保留原绑定并给出明确提示
|
||||||
self._apply_bind_info_to_account(account, before_bound or before, "game_queried")
|
if before_bound is not None:
|
||||||
|
self._apply_bind_info_to_account(account, before_bound, "game_queried")
|
||||||
|
else:
|
||||||
|
account.bind_status = "game_queried"
|
||||||
|
account.updated_at = datetime.now(timezone.utc)
|
||||||
self._mark_task(
|
self._mark_task(
|
||||||
db,
|
db,
|
||||||
task,
|
task,
|
||||||
@@ -1223,18 +1250,11 @@ class DouyuBatchRunner:
|
|||||||
f"error={confirm_raw.get('error')} msg={confirm_raw.get('msg') or '-'}",
|
f"error={confirm_raw.get('error')} msg={confirm_raw.get('msg') or '-'}",
|
||||||
)
|
)
|
||||||
def _pick_bound_after(candidates: list[dict]) -> dict | None:
|
def _pick_bound_after(candidates: list[dict]) -> dict | None:
|
||||||
"""确认后回查:只认已生效绑定(bound_act=1)。
|
"""确认后回查:只认活动 alias 上的已生效绑定(bound_act=1)。"""
|
||||||
|
return self._pick_current_bound_info(
|
||||||
cjm 的角色名恒有且 bound_act 恒为 0,若按 alias 顺序挑选会永远
|
candidates,
|
||||||
判定“确认未生效”,必须按 bound_act=1 判定确认结果。
|
config,
|
||||||
"""
|
extra_prefer_aliases=[use_confirm_alias],
|
||||||
return next(
|
|
||||||
(
|
|
||||||
info
|
|
||||||
for info in candidates
|
|
||||||
if self._is_bound_act(info) and str(info.get("role_name") or "").strip()
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1255,7 +1275,11 @@ class DouyuBatchRunner:
|
|||||||
break
|
break
|
||||||
if after is None:
|
if after is None:
|
||||||
# 已生效绑定始终未出现:确认未生效(或同步延迟超时),保留原绑定
|
# 已生效绑定始终未出现:确认未生效(或同步延迟超时),保留原绑定
|
||||||
self._apply_bind_info_to_account(account, before_bound or before, "game_queried")
|
if before_bound is not None:
|
||||||
|
self._apply_bind_info_to_account(account, before_bound, "game_queried")
|
||||||
|
else:
|
||||||
|
account.bind_status = "game_queried"
|
||||||
|
account.updated_at = datetime.now(timezone.utc)
|
||||||
self._mark_task(
|
self._mark_task(
|
||||||
db,
|
db,
|
||||||
task,
|
task,
|
||||||
@@ -1284,7 +1308,11 @@ class DouyuBatchRunner:
|
|||||||
except DouyuActivityError as exc:
|
except DouyuActivityError as exc:
|
||||||
# 查询接口异常:确认接口已成功时按成功处理,但保留错误信息。
|
# 查询接口异常:确认接口已成功时按成功处理,但保留错误信息。
|
||||||
# 写库优先确认前已生效绑定,避免待确认角色被误写入。
|
# 写库优先确认前已生效绑定,避免待确认角色被误写入。
|
||||||
self._apply_bind_info_to_account(account, before_bound or before, "bind_confirmed")
|
if before_bound is not None:
|
||||||
|
self._apply_bind_info_to_account(account, before_bound, "bind_confirmed")
|
||||||
|
else:
|
||||||
|
account.bind_status = "bind_confirmed"
|
||||||
|
account.updated_at = datetime.now(timezone.utc)
|
||||||
self._mark_task(
|
self._mark_task(
|
||||||
db,
|
db,
|
||||||
task,
|
task,
|
||||||
@@ -2006,14 +2034,7 @@ class DouyuBatchRunner:
|
|||||||
|
|
||||||
# 1) 优先“已生效绑定”角色(bound_act=1 且有角色名,通常是活动 alias)。
|
# 1) 优先“已生效绑定”角色(bound_act=1 且有角色名,通常是活动 alias)。
|
||||||
# 避免把扫码后未确认的新角色当成当前绑定结果。
|
# 避免把扫码后未确认的新角色当成当前绑定结果。
|
||||||
bound_info = next(
|
bound_info = self._pick_current_bound_info(candidates, config)
|
||||||
(
|
|
||||||
info
|
|
||||||
for info in candidates
|
|
||||||
if self._is_bound_act(info) and str(info.get("role_name") or "").strip()
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
# 2) 待确认角色(cjm 扫码后未确认;无已绑定时也用于首次绑定展示)
|
# 2) 待确认角色(cjm 扫码后未确认;无已绑定时也用于首次绑定展示)
|
||||||
pending_info = self._pick_bind_info(
|
pending_info = self._pick_bind_info(
|
||||||
candidates,
|
candidates,
|
||||||
@@ -2089,30 +2110,15 @@ class DouyuBatchRunner:
|
|||||||
"""换绑倒计时优先看活动当前绑定(QYOOB),不是 cjm 换绑最新态。"""
|
"""换绑倒计时优先看活动当前绑定(QYOOB),不是 cjm 换绑最新态。"""
|
||||||
if not candidates:
|
if not candidates:
|
||||||
return None
|
return None
|
||||||
# 1) 优先活动 alias 上有角色的结果
|
# 冷却是“当前已生效绑定”的属性,不能用 legacy/cjm 的待确认角色判断。
|
||||||
baseline = self._pick_baseline_bind_info(candidates, config)
|
current_bound = self._pick_current_bound_info(candidates, config)
|
||||||
if baseline is not None:
|
if current_bound is not None:
|
||||||
wait = self._to_int(baseline.get("change_role_wait_time"))
|
return current_bound
|
||||||
if wait is not None:
|
action_aliases = self._action_act_aliases(config)
|
||||||
return baseline
|
return self._pick_bind_info(
|
||||||
# 2) 任意带 wait_time 的结果里取 wait 最大的
|
[info for info in candidates if str(info.get("act_alias") or "") in action_aliases],
|
||||||
with_wait = []
|
|
||||||
for info in candidates:
|
|
||||||
wait = self._to_int(info.get("change_role_wait_time"))
|
|
||||||
if wait is not None:
|
|
||||||
with_wait.append((wait, info))
|
|
||||||
if with_wait:
|
|
||||||
with_wait.sort(key=lambda item: item[0], reverse=True)
|
|
||||||
return with_wait[0][1]
|
|
||||||
# 3) 回退 baseline / 首个有角色
|
|
||||||
return baseline or self._pick_bind_info(
|
|
||||||
candidates,
|
|
||||||
prefer_pending=False,
|
prefer_pending=False,
|
||||||
prefer_aliases=[
|
prefer_aliases=action_aliases,
|
||||||
str(config.get("confirm_act_alias") or "").strip(),
|
|
||||||
str(config.get("bind_act_alias") or "").strip(),
|
|
||||||
str(config.get("legacy_act_alias") or "").strip(),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def _execute_query_change_bind_time(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
def _execute_query_change_bind_time(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
||||||
|
|||||||
@@ -334,6 +334,29 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
|||||||
return map;
|
return map;
|
||||||
}, [tasks]);
|
}, [tasks]);
|
||||||
|
|
||||||
|
const latestConfirmBindTaskByAccount = useMemo(() => {
|
||||||
|
const map = new Map<number, DouyuTaskItem>();
|
||||||
|
for (const task of tasks) {
|
||||||
|
if (task.task_type !== 'confirm_bind') continue;
|
||||||
|
if (!task.result) continue;
|
||||||
|
const previous = map.get(task.account_id);
|
||||||
|
if (!previous || task.id > previous.id) map.set(task.account_id, task);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, [tasks]);
|
||||||
|
|
||||||
|
const latestConfirmBindFailureByAccount = useMemo(() => {
|
||||||
|
const map = new Map<number, DouyuTaskItem>();
|
||||||
|
for (const task of tasks) {
|
||||||
|
if (task.task_type !== 'confirm_bind') continue;
|
||||||
|
if (!['failed', 'error'].includes(task.status)) continue;
|
||||||
|
if (!(task.message || '').includes('待绑定游戏账号')) continue;
|
||||||
|
const previous = map.get(task.account_id);
|
||||||
|
if (!previous || task.id > previous.id) map.set(task.account_id, task);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, [tasks]);
|
||||||
|
|
||||||
const latestEsportsStateTaskByAccount = useMemo(() => {
|
const latestEsportsStateTaskByAccount = useMemo(() => {
|
||||||
const map = new Map<number, DouyuTaskItem>();
|
const map = new Map<number, DouyuTaskItem>();
|
||||||
for (const task of tasks) {
|
for (const task of tasks) {
|
||||||
@@ -356,7 +379,7 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
|||||||
[tasks, activeQrTaskId],
|
[tasks, activeQrTaskId],
|
||||||
);
|
);
|
||||||
|
|
||||||
// 换绑时间:优先最近一次查询/拦截任务结果,回退账号落库字段
|
// 换绑时间:优先最近一次查询/拦截/确认成功结果,回退账号落库字段
|
||||||
const latestChangeWaitByAccount = useMemo(() => {
|
const latestChangeWaitByAccount = useMemo(() => {
|
||||||
const map = new Map<number, {
|
const map = new Map<number, {
|
||||||
wait: number | null; canChangeTime: number | null; text: string; taskId: number;
|
wait: number | null; canChangeTime: number | null; text: string; taskId: number;
|
||||||
@@ -364,9 +387,20 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
|||||||
for (const task of tasks) {
|
for (const task of tasks) {
|
||||||
const taskTypes = isEsportsHandbook
|
const taskTypes = isEsportsHandbook
|
||||||
? ['prepare_esports_bind', 'get_esports_bind_qr', 'query_esports_game_name', 'confirm_esports_bind']
|
? ['prepare_esports_bind', 'get_esports_bind_qr', 'query_esports_game_name', 'confirm_esports_bind']
|
||||||
: ['query_change_bind_time', 'get_bind_qr'];
|
: ['query_change_bind_time', 'get_bind_qr', 'confirm_bind'];
|
||||||
if (!taskTypes.includes(task.task_type)) continue;
|
if (!taskTypes.includes(task.task_type)) continue;
|
||||||
if (!['success', 'failed'].includes(task.status)) continue;
|
if (!['success', 'failed'].includes(task.status)) continue;
|
||||||
|
if (
|
||||||
|
!isEsportsHandbook
|
||||||
|
&& task.task_type === 'confirm_bind'
|
||||||
|
&& (
|
||||||
|
task.status !== 'success'
|
||||||
|
|| !(
|
||||||
|
resultFlag(task.result, 'bind_confirmed')
|
||||||
|
|| resultFlag(task.result, 'is_bound_act')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) continue;
|
||||||
const wait = resultNumber(task.result, 'change_role_wait_time');
|
const wait = resultNumber(task.result, 'change_role_wait_time');
|
||||||
const canChangeTime = resultNumber(task.result, 'can_change_time');
|
const canChangeTime = resultNumber(task.result, 'can_change_time');
|
||||||
// get_bind_qr 只有冷却拦截时才有 wait;无 wait 时跳过,避免覆盖
|
// get_bind_qr 只有冷却拦截时才有 wait;无 wait 时跳过,避免覆盖
|
||||||
@@ -644,6 +678,14 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
|||||||
for (const task of failedTasks) autoNotifiedConfirmFailedTaskIds.current.add(task.id);
|
for (const task of failedTasks) autoNotifiedConfirmFailedTaskIds.current.add(task.id);
|
||||||
const latest = failedTasks[0];
|
const latest = failedTasks[0];
|
||||||
setConfirmFailTip({ taskId: latest.id, message: latest.message });
|
setConfirmFailTip({ taskId: latest.id, message: latest.message });
|
||||||
|
setQrTaskIds((prev) => {
|
||||||
|
const next = prev.filter((id) => {
|
||||||
|
const task = visibleTasks.find((item) => item.id === id);
|
||||||
|
return !task || task.account_id !== latest.account_id;
|
||||||
|
});
|
||||||
|
setActiveQrTaskId((cur) => (cur != null && !next.includes(cur) ? (next[next.length - 1] ?? null) : cur));
|
||||||
|
return next;
|
||||||
|
});
|
||||||
}, [visibleTasks]);
|
}, [visibleTasks]);
|
||||||
|
|
||||||
// 弹窗倒计时自动关闭
|
// 弹窗倒计时自动关闭
|
||||||
@@ -1019,32 +1061,76 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '游戏名', dataIndex: 'game_name', width: 130,
|
title: '游戏名', dataIndex: 'game_name', width: 170,
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
const queryResult = (
|
const queryTask = isEsportsHandbook
|
||||||
isEsportsHandbook
|
? latestEsportsStateTaskByAccount.get(record.id) || null
|
||||||
? latestEsportsStateTaskByAccount.get(record.id)
|
: latestQueryGameTaskByAccount.get(record.id) || null;
|
||||||
: latestQueryGameTaskByAccount.get(record.id)
|
const confirmTask = isEsportsHandbook ? null : latestConfirmBindTaskByAccount.get(record.id) || null;
|
||||||
)?.result || null;
|
const confirmSucceeded = Boolean(
|
||||||
// role_name = 当前已生效绑定角色;pending_role_name = 扫码后待确认的新角色
|
confirmTask
|
||||||
const roleName = resultText(queryResult, 'role_name')
|
&& confirmTask.status === 'success'
|
||||||
|| (isEsportsHandbook ? record.esports_game_name : record.game_name);
|
&& (
|
||||||
const pendingRoleName = resultText(queryResult, 'pending_role_name');
|
resultFlag(confirmTask.result, 'bind_confirmed')
|
||||||
const channel = (
|
|| resultFlag(confirmTask.result, 'is_bound_act')
|
||||||
[resultText(queryResult, 'area_name'), resultText(queryResult, 'plat_name')]
|
),
|
||||||
.filter(Boolean)
|
|
||||||
.join(' / ')
|
|
||||||
|| (isEsportsHandbook ? record.esports_game_channel : record.game_channel)
|
|
||||||
);
|
);
|
||||||
return roleName ? (
|
const stateTask = (
|
||||||
<Space direction="vertical" size={0}>
|
confirmSucceeded
|
||||||
<Text>{roleName}</Text>
|
&& (!queryTask || confirmTask!.id > queryTask.id)
|
||||||
|
) ? confirmTask : queryTask;
|
||||||
|
const confirmFailureTask = latestConfirmBindFailureByAccount.get(record.id);
|
||||||
|
const clearPending = Boolean(
|
||||||
|
!isEsportsHandbook
|
||||||
|
&& confirmFailureTask
|
||||||
|
&& stateTask
|
||||||
|
&& confirmFailureTask.id > stateTask.id,
|
||||||
|
);
|
||||||
|
const stateResult = stateTask?.result || null;
|
||||||
|
const stateRoleConfirmed = resultFlag(stateResult, 'is_bound_act') || resultFlag(stateResult, 'bind_confirmed');
|
||||||
|
const stateRoleName = clearPending && !stateRoleConfirmed ? '' : resultText(stateResult, 'role_name');
|
||||||
|
// role_name = 当前已生效绑定角色;pending_role_name = 扫码后待确认的新角色
|
||||||
|
const roleName = stateRoleName
|
||||||
|
|| (isEsportsHandbook ? record.esports_game_name : record.game_name);
|
||||||
|
const stateChannel = clearPending && !stateRoleConfirmed
|
||||||
|
? ''
|
||||||
|
: [resultText(stateResult, 'area_name'), resultText(stateResult, 'plat_name')]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' / ');
|
||||||
|
const pendingRoleName = clearPending || confirmSucceeded ? '' : resultText(stateResult, 'pending_role_name');
|
||||||
|
const channel = stateChannel || (isEsportsHandbook ? record.esports_game_channel : record.game_channel);
|
||||||
|
const pendingChannel = clearPending || confirmSucceeded ? '' : [resultText(stateResult, 'pending_area_name'), resultText(stateResult, 'pending_plat_name')]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' / ');
|
||||||
|
if (!roleName && !pendingRoleName) return <Text type="secondary">未查</Text>;
|
||||||
|
return (
|
||||||
|
<Space direction="vertical" size={2} style={{ lineHeight: 1.35 }}>
|
||||||
|
{roleName ? (
|
||||||
|
<Space direction="vertical" size={0}>
|
||||||
|
<Text ellipsis title={roleName}>
|
||||||
|
{pendingRoleName ? `当前: ${roleName}` : roleName}
|
||||||
|
</Text>
|
||||||
|
{channel ? (
|
||||||
|
<Text type="secondary" style={{ fontSize: 12 }} ellipsis title={channel}>
|
||||||
|
{channel}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
|
</Space>
|
||||||
|
) : null}
|
||||||
{pendingRoleName ? (
|
{pendingRoleName ? (
|
||||||
<Text type="warning" style={{ fontSize: 12 }}>待确认: {pendingRoleName}</Text>
|
<Space direction="vertical" size={0}>
|
||||||
|
<Text type="warning" style={{ fontSize: 12 }} ellipsis title={pendingRoleName}>
|
||||||
|
待确认: {pendingRoleName}
|
||||||
|
</Text>
|
||||||
|
{pendingChannel ? (
|
||||||
|
<Text type="warning" style={{ fontSize: 12 }} ellipsis title={pendingChannel}>
|
||||||
|
{pendingChannel}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
|
</Space>
|
||||||
) : null}
|
) : null}
|
||||||
{channel ? <Text type="secondary" style={{ fontSize: 12 }}>{channel}</Text> : null}
|
|
||||||
</Space>
|
</Space>
|
||||||
) : <Text type="secondary">未查</Text>;
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user