优化斗鱼绑定状态同步

This commit is contained in:
yml2213
2026-08-02 17:54:36 +08:00
parent c62f52a067
commit a466e11c9c
4 changed files with 200 additions and 108 deletions
+109 -23
View File
@@ -334,6 +334,29 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
return map;
}, [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 map = new Map<number, DouyuTaskItem>();
for (const task of tasks) {
@@ -356,7 +379,7 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
[tasks, activeQrTaskId],
);
// 换绑时间:优先最近一次查询/拦截任务结果,回退账号落库字段
// 换绑时间:优先最近一次查询/拦截/确认成功结果,回退账号落库字段
const latestChangeWaitByAccount = useMemo(() => {
const map = new Map<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) {
const taskTypes = isEsportsHandbook
? ['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 (!['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 canChangeTime = resultNumber(task.result, 'can_change_time');
// 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);
const latest = failedTasks[0];
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]);
// 弹窗倒计时自动关闭
@@ -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) => {
const queryResult = (
isEsportsHandbook
? latestEsportsStateTaskByAccount.get(record.id)
: latestQueryGameTaskByAccount.get(record.id)
)?.result || null;
// role_name = 当前已生效绑定角色;pending_role_name = 扫码后待确认的新角色
const roleName = resultText(queryResult, 'role_name')
|| (isEsportsHandbook ? record.esports_game_name : record.game_name);
const pendingRoleName = resultText(queryResult, 'pending_role_name');
const channel = (
[resultText(queryResult, 'area_name'), resultText(queryResult, 'plat_name')]
.filter(Boolean)
.join(' / ')
|| (isEsportsHandbook ? record.esports_game_channel : record.game_channel)
const queryTask = isEsportsHandbook
? latestEsportsStateTaskByAccount.get(record.id) || null
: latestQueryGameTaskByAccount.get(record.id) || null;
const confirmTask = isEsportsHandbook ? null : latestConfirmBindTaskByAccount.get(record.id) || null;
const confirmSucceeded = Boolean(
confirmTask
&& confirmTask.status === 'success'
&& (
resultFlag(confirmTask.result, 'bind_confirmed')
|| resultFlag(confirmTask.result, 'is_bound_act')
),
);
return roleName ? (
<Space direction="vertical" size={0}>
<Text>{roleName}</Text>
const stateTask = (
confirmSucceeded
&& (!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 ? (
<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}
{channel ? <Text type="secondary" style={{ fontSize: 12 }}>{channel}</Text> : null}
</Space>
) : <Text type="secondary"></Text>;
);
},
},
{