优化客服CK重登流程

This commit is contained in:
yml2213
2026-08-14 14:43:21 +08:00
parent 1ed1eac205
commit bf2f78aa70
10 changed files with 550 additions and 162 deletions
+4
View File
@@ -26,6 +26,10 @@ export const cookieApi = {
api.post<{ batch_id: string; count: number; skipped: number; success: boolean }, { batch_id: string; count: number; skipped: number; success: boolean }>('/cookies/relogin', { ids }),
reloginOperations: (ids: number[]) =>
api.post<{ batch_id: string; count: number; skipped: number; success: boolean }, { batch_id: string; count: number; skipped: number; success: boolean }>('/cookies/operations/relogin', { ids }),
reloginInvalidOperations: (params?: { search?: string; tag?: string }) =>
api.post<{ batch_id: string; count: number; skipped: number; success: boolean }, { batch_id: string; count: number; skipped: number; success: boolean }>('/cookies/operations/relogin-invalid', null, { params }),
stopOperationRelogin: (batchId: string) =>
api.post<MessageResponse, MessageResponse>(`/cookies/operations/relogin/${batchId}/stop`),
loginTasks: (batchId: string) =>
api.get<LoginTaskItem[], LoginTaskItem[]>('/login/tasks', { params: { batch_id: batchId } }),
delete: (id: number) => api.delete<MessageResponse, MessageResponse>(`/cookies/${id}`),
+3
View File
@@ -280,6 +280,9 @@ export interface CookieOperationItem {
ck_check_status: string;
ck_checked_at: string | null;
created_at: string | null;
relogin_status: '' | 'relogin_pending' | 'relogin_running' | 'relogin_failed';
relogin_message: string;
relogin_batch_id: string;
}
export interface CookieOperationCheckResult {
+4
View File
@@ -22,6 +22,10 @@ const ACTION_OPTIONS = [
{ value: 'recharge:huya:create', label: '虎牙创建充值批次' },
{ value: 'recharge:huya:stop', label: '虎牙停止充值批次' },
{ value: 'recharge:huya:config', label: '虎牙充值配置' },
{ value: 'cookie:check', label: '检测账号 CK' },
{ value: 'cookie:relogin', label: '创建 CK 重登批次' },
{ value: 'cookie:relogin_invalid', label: '批量重登失效 CK' },
{ value: 'cookie:relogin_stop', label: '停止 CK 重登批次' },
];
const ACTION_LABELS = new Map(ACTION_OPTIONS.map((item) => [item.value, item.label]));
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useState } from 'react';
import { Button, Input, Popconfirm, Select, Space, Table, Tag, Typography } from 'antd';
import { message } from '../utils/antdMessage';
import { LoadingOutlined, ReloadOutlined, SearchOutlined } from '@ant-design/icons';
import { LoadingOutlined, ReloadOutlined, SearchOutlined, StopOutlined } from '@ant-design/icons';
import { cookieApi, type CookieOperationCheckResult, type CookieOperationItem } from '../api/modules';
import { formatTime } from '../utils/time';
import { getErrorMessage } from '../utils/error';
@@ -59,6 +59,20 @@ export default function CookieOperationsPage() {
void loadItems();
}, [loadItems]);
const hasActiveRelogin = items.some((item) => (
item.relogin_status === 'relogin_pending' || item.relogin_status === 'relogin_running'
));
const activeReloginBatchIds = Array.from(new Set(items
.filter((item) => item.relogin_status === 'relogin_pending' || item.relogin_status === 'relogin_running')
.map((item) => item.relogin_batch_id)
.filter(Boolean)));
useEffect(() => {
if (!hasActiveRelogin) return;
const timer = window.setInterval(() => void loadItems(), 2000);
return () => window.clearInterval(timer);
}, [hasActiveRelogin, loadItems]);
useEffect(() => {
cookieApi.listOperationTags().then(setTags).catch(() => {
// 标签加载失败不影响 CK 检测与重登。
@@ -108,8 +122,41 @@ export default function CookieOperationsPage() {
}
};
const handleReloginInvalid = async () => {
try {
const response = await cookieApi.reloginInvalidOperations({
search: search.trim() || undefined,
tag: tagFilter || undefined,
});
const skipped = response.skipped ? `${response.skipped} 条因缺少登录凭据跳过` : '';
message.success(`已开始重登 ${response.count} 个失效账号${skipped}`);
setSelectedRowKeys([]);
void loadItems();
} catch (error: unknown) {
message.error(getErrorMessage(error));
}
};
const handleStopRelogin = async () => {
if (activeReloginBatchIds.length === 0) return;
try {
await Promise.all(activeReloginBatchIds.map((batchId) => cookieApi.stopOperationRelogin(batchId)));
message.success('已发送停止信号');
void loadItems();
} catch (error: unknown) {
message.error(getErrorMessage(error));
}
};
const resultFor = (item: CookieOperationItem) => checkResults.get(item.id);
const selectedIds = selectedRowKeys.map((key) => Number(key));
const reloginStatus = (item: CookieOperationItem) => {
if (item.relogin_status === 'relogin_pending') return <Tag color="processing"></Tag>;
if (item.relogin_status === 'relogin_running') return <Tag color="processing" icon={<LoadingOutlined />}></Tag>;
if (item.relogin_status === 'relogin_failed') return <Tag color="error"></Tag>;
if (item.relogin_message.startsWith('重新登录成功')) return <Tag color="success"></Tag>;
return <Text type="secondary">-</Text>;
};
const columns = [
{ title: '账号', dataIndex: 'account_username', ellipsis: true },
{
@@ -137,6 +184,20 @@ export default function CookieOperationsPage() {
return checkedAt ? formatTime(checkedAt) : <Text type="secondary">-</Text>;
},
},
{
title: '重登状态',
width: 130,
render: (_: unknown, item: CookieOperationItem) => (
<Space direction="vertical" size={0}>
{reloginStatus(item)}
{item.relogin_status === 'relogin_failed' && item.relogin_message ? (
<Text type="secondary" ellipsis style={{ maxWidth: 180, fontSize: 12 }} title={item.relogin_message}>
{item.relogin_message}
</Text>
) : null}
</Space>
),
},
{
title: '操作',
width: 190,
@@ -145,7 +206,7 @@ export default function CookieOperationsPage() {
<Button
size="small"
loading={checkingIds.has(item.id)}
disabled={checkingIds.has(item.id)}
disabled={checkingIds.has(item.id) || item.relogin_status === 'relogin_pending' || item.relogin_status === 'relogin_running'}
onClick={() => void handleCheck([item.id])}
>
@@ -155,7 +216,7 @@ export default function CookieOperationsPage() {
size="small"
icon={<ReloadOutlined />}
loading={reloginIds.has(item.id)}
disabled={reloginIds.has(item.id)}
disabled={reloginIds.has(item.id) || item.relogin_status === 'relogin_pending' || item.relogin_status === 'relogin_running'}
>
</Button>
@@ -182,6 +243,19 @@ export default function CookieOperationsPage() {
{selectedIds.length > 0 ? `(${selectedIds.length})` : ''}
</Button>
</Popconfirm>
<Popconfirm
title="将重新登录当前筛选范围内所有已检测为无效的账号,并在成功后替换旧 CK,确认继续?"
onConfirm={() => void handleReloginInvalid()}
>
<Button icon={<ReloadOutlined />}>
</Button>
</Popconfirm>
{activeReloginBatchIds.length > 0 ? (
<Popconfirm title="确认停止当前重登任务?旧 CK 会保留。" onConfirm={() => void handleStopRelogin()}>
<Button danger icon={<StopOutlined />}></Button>
</Popconfirm>
) : null}
</Space>
</div>
<Space wrap style={{ marginBottom: 12 }}>