import { useCallback, useEffect, useState } from 'react'; import { Button, Card, Descriptions, Drawer, Input, Select, Space, Table, Tag, Typography } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { ReloadOutlined } from '@ant-design/icons'; import { auditApi, type AuditLogEntry, type AuditLogQuery } from '../api/modules'; import { getErrorMessage } from '../utils/error'; import { message } from '../utils/antdMessage'; import { formatTime } from '../utils/time'; const { Text } = Typography; const ACTION_OPTIONS = [ { value: 'recharge:yyb:create', label: '应用宝创建任务' }, { value: 'recharge:yyb:login', label: '应用宝发起登录' }, { value: 'recharge:yyb:selection', label: '应用宝选择商品角色' }, { value: 'recharge:yyb:payment', label: '应用宝生成付款码' }, { value: 'recharge:yyb:payment_check', label: '应用宝检测到账' }, { value: 'recharge:yyb:stop', label: '应用宝停止任务' }, { value: 'recharge:douyu:create', label: '斗鱼创建充值批次' }, { value: 'recharge:douyu:callback', label: '斗鱼供应商回调' }, { value: 'recharge:douyu:stop', label: '斗鱼停止直充批次' }, { value: 'recharge:douyu:config', label: '斗鱼直充配置' }, { 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])); function actionLabel(action: string): string { return ACTION_LABELS.get(action) || action; } function paymentMethod(detail: string): string { try { const data = JSON.parse(detail) as Record; return typeof data.payment_method === 'string' ? data.payment_method : '-'; } catch { return '-'; } } function detailSummary(detail: string): string { try { const data = JSON.parse(detail) as Record; const rechargeAccounts = Array.isArray(data.recharge_accounts) ? data.recharge_accounts : []; const supplierOrders = Array.isArray(data.supplier_orders) ? data.supplier_orders : []; const supplierOrderIds = Array.isArray(data.supplier_order_ids) ? data.supplier_order_ids : []; const supplierOutOrderIds = Array.isArray(data.supplier_out_order_ids) ? data.supplier_out_order_ids : []; // 优先直观展示充值账号与供应商单号 if (supplierOrderIds.length > 0 || supplierOutOrderIds.length > 0 || supplierOrders.length > 0) { const names = rechargeAccounts.slice(0, 2).map((item) => { const account = item as Record; return String(account.douyu_nickname || account.username || account.douyu_uid || '-'); }); const accText = names.length > 0 ? `充值账号: ${names.join('、')}${rechargeAccounts.length > 2 ? ` 等(${rechargeAccounts.length})` : ''}` : ''; const orderId = String(supplierOrderIds[0] || (supplierOrders[0] as Record)?.supplier_order_id || '').trim(); const outId = String(supplierOutOrderIds[0] || (supplierOrders[0] as Record)?.supplier_out_order_id || '').trim(); const orderParts: string[] = []; if (orderId) orderParts.push(`供应商单号: ${orderId}`); if (outId && outId !== orderId) orderParts.push(`外部单号: ${outId}`); return [accText, orderParts.join(' | ')].filter(Boolean).join(' | '); } if (rechargeAccounts.length > 0) { const names = rechargeAccounts.slice(0, 3).map((item) => { const account = item as Record; return String(account.douyu_nickname || account.username || account.douyu_uid || '-'); }); return `充值账号: ${names.join('、')}${rechargeAccounts.length > 3 ? ' 等' : ''}`; } if (data.out_order_id || data.order_id) { const parts: string[] = []; if (data.order_id) parts.push(`供应商单号: ${String(data.order_id)}`); if (data.out_order_id) parts.push(`外部单号: ${String(data.out_order_id)}`); if (data.message) parts.push(String(data.message)); return parts.join(' | '); } return Object.entries(data) .filter(([key]) => key !== 'message') .slice(0, 4) .map(([key, value]) => `${key}: ${String(value)}`) .join(' | ') || String(data.message || ''); } catch { return detail; } } function formatDetail(detail: string): string { try { return JSON.stringify(JSON.parse(detail), null, 2); } catch { return detail || '-'; } } function parseDetailData(detail: string): Record | null { try { const data = JSON.parse(detail); return typeof data === 'object' && data !== null ? (data as Record) : null; } catch { return null; } } export default function AuditLogsPage() { const [rows, setRows] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const [query, setQuery] = useState({ page: 1, page_size: 50 }); const [selected, setSelected] = useState(null); const load = useCallback(async (nextQuery = query) => { setLoading(true); try { const data = await auditApi.list(nextQuery); setRows(data.items); setTotal(data.total); } catch (error: unknown) { message.error(getErrorMessage(error)); } finally { setLoading(false); } }, [query]); useEffect(() => { void load(); }, [load]); const updateFilter = (changes: Partial) => { const next = { ...query, ...changes, page: 1 }; setQuery(next); void load(next); }; const columns: ColumnsType = [ { title: '时间', dataIndex: 'created_at', width: 180, render: formatTime }, { title: '操作者', dataIndex: 'username', width: 120, render: (value) => value || '-' }, { title: '操作', dataIndex: 'action', width: 180, render: actionLabel }, { title: '目标', dataIndex: 'target', width: 210, ellipsis: true }, { title: '充值方式', dataIndex: 'detail', width: 130, render: (detail: string) => paymentMethod(detail), }, { title: '结果', dataIndex: 'success', width: 88, render: (value: boolean | null) => ( value === null ? 历史记录 : {value ? '成功' : '失败'} ), }, { title: '摘要', dataIndex: 'detail', ellipsis: true, render: (value: string) => {detailSummary(value)}, }, ]; return ( } onClick={() => void load()} loading={loading}>刷新}> updateFilter({ username: event.currentTarget.value || undefined })} onBlur={(event) => updateFilter({ username: event.currentTarget.value || undefined })} /> updateFilter({ success: value === undefined ? undefined : value === 'true' })} /> updateFilter({ keyword: value || undefined })} /> ({ onClick: () => setSelected(record), style: { cursor: 'pointer' } })} pagination={{ current: query.page, pageSize: query.page_size, total, showSizeChanger: true, showTotal: (count) => `共 ${count} 条`, onChange: (page, pageSize) => { const next = { ...query, page, page_size: pageSize }; setQuery(next); void load(next); }, }} /> setSelected(null)} width={640}> {selected && (() => { const detailData = parseDetailData(selected.detail); const supplierOrders = Array.isArray(detailData?.supplier_orders) ? (detailData?.supplier_orders as Array>) : []; const supplierOrderIds = Array.isArray(detailData?.supplier_order_ids) ? (detailData?.supplier_order_ids as string[]) : []; const supplierOutOrderIds = Array.isArray(detailData?.supplier_out_order_ids) ? (detailData?.supplier_out_order_ids as string[]) : []; return ( {formatTime(selected.created_at)} {selected.username || '-'} {actionLabel(selected.action)} {selected.target || '-'} {paymentMethod(selected.detail)} {selected.success === null ? '历史记录' : selected.success ? '成功' : '失败'} {supplierOrderIds.length > 0 && ( {supplierOrderIds.map((id) => (
{id}
))}
)} {supplierOutOrderIds.length > 0 && ( {supplierOutOrderIds.map((id) => (
{id}
))}
)}
{supplierOrders.length > 0 && ( > size="small" pagination={false} rowKey={(r) => String(r.task_id || r.supplier_out_order_id || Math.random())} dataSource={supplierOrders} columns={[ { title: '充值账号', key: 'account', render: (_: unknown, r: Record) => (
{String(r.douyu_nickname || r.username || '-')}
{Boolean(r.douyu_uid) && UID: {String(r.douyu_uid)}}
), }, { title: '数量', dataIndex: 'amount', key: 'amount', width: 90, render: (v: unknown) => (v ? `${String(v)} 鱼翅` : '-'), }, { title: '供应商单号', dataIndex: 'supplier_order_id', key: 'supplier_order_id', render: (v: unknown) => (v ? {String(v)} : '-'), }, { title: '外部单号', dataIndex: 'supplier_out_order_id', key: 'supplier_out_order_id', render: (v: unknown) => (v ? {String(v)} : '-'), }, { title: '状态', dataIndex: 'status', key: 'status', width: 80, render: (v: unknown) => { const s = String(v || ''); if (s === 'success') return 成功; if (s === 'failed') return 失败; if (s === 'running') return 处理中; return {s || '-'}; }, }, ]} />
)}
                  {formatDetail(selected.detail)}
                
); })()}
); }