/** * filekorolheader: 操作日志页面 - 用户操作行为监控页面 * 功能:操作日志查询、统计、导出、筛选 * 路径:/central-config/monitor/operation-log * 规范:遵循crop-x/docs/开发项目规范.md,使用SearchFormPagination重构,事件驱动模式 */ 'use client'; import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Download, Eye, CheckCircle, XCircle, Clock, AlertTriangle } from 'lucide-react'; import { SearchFormPagination, type SearchFieldConfig, type TableColumnConfig } from '@/components/common/searchFormPagination'; import { fetchOperationLogs, transformOperationLogsList, OperationLog, PaginationState, OperationLogsQueryParams, exportOperationLogs } from './components'; export default function OperationLogPage() { const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(false); const [pagination, setPagination] = useState({ page: 1, size: 10, total: 0, totalPages: 0, hasNext: false, hasPrev: false, }); const [searchFilters, setSearchFilters] = useState>({ search: '', module: 'all', status: 'all' }); const isFirstLoad = useRef(true); // 搜索字段配置 const searchFields: SearchFieldConfig[] = [ { key: 'search', label: '搜索', type: 'text', placeholder: '搜索操作人、模块、操作...', }, { key: 'module', label: '模块', type: 'select', defaultValue: 'all', options: [ { value: 'all', label: '全部模块' }, { value: '用户管理', label: '用户管理' }, { value: '企业管理', label: '企业管理' }, { value: '系统配置', label: '系统配置' }, { value: '数据管理', label: '数据管理' }, ], }, { key: 'status', label: '状态', type: 'select', defaultValue: 'all', options: [ { value: 'all', label: '全部状态' }, { value: 'success', label: '成功' }, { value: 'failed', label: '失败' }, ], }, ]; // 表格列配置 const columns: TableColumnConfig[] = [ { key: 'created_at', label: '操作时间', render: (value: string) => (
{value ? new Date(value).toLocaleString('zh-CN') : '-'}
), }, { key: 'username', label: '操作人', render: (value: string) => (
{value || '-'}
), }, { key: 'module', label: '模块', render: (value: string) => ( {value || '-'} ), }, { key: 'action', label: '操作', render: (value: string) => (
{value || '-'}
), }, { key: 'processing_time', label: '耗时', render: (value: number) => (
{value ? `${(value * 1000).toFixed(0)}ms` : '-'}
), }, { key: 'response_status', label: '状态', render: (value: number) => { if (value >= 200 && value < 300) { return ( 成功 ); } else { return ( 失败 ); } }, }, { key: 'actions', label: '操作', render: (_: any, row: OperationLog) => (
), }, ]; // 加载操作日志数据 - 事件驱动模式 const loadOperationLogs = useCallback(async (params?: { filters?: Record; pagination?: { page: number; size: number }; resetPage?: boolean; }) => { try { setLoading(true); const queryParams: OperationLogsQueryParams = { page: params?.resetPage ? 1 : (params?.pagination?.page || pagination.page), size: params?.pagination?.size || pagination.size, sort_order: 'desc', order_by: 'created_at', }; // 处理搜索条件 const searchKeyword = params?.filters?.search ?? searchFilters.search; if (searchKeyword) { queryParams.username = searchKeyword; queryParams.module = searchKeyword; queryParams.action = searchKeyword; } // 处理模块筛选 const module = params?.filters?.module ?? searchFilters.module; if (module !== 'all') { queryParams.module = module; } // 处理状态筛选 const status = params?.filters?.status ?? searchFilters.status; if (status !== 'all') { queryParams.response_status = status === 'success' ? 200 : 400; } const response = await fetchOperationLogs(queryParams); const transformedLogs = transformOperationLogsList(response.data); setLogs(transformedLogs); setPagination({ page: response.page, size: response.size, total: response.total, totalPages: response.totalPages, hasNext: response.hasNext, hasPrev: response.hasPrev, }); } catch (error) { console.error('Failed to load operation logs:', error); const errorMessage = error instanceof Error ? error.message : '加载操作日志失败'; toast.error(errorMessage); } finally { setLoading(false); } }, [pagination.page, pagination.size, searchFilters]); // 初始化数据 - 只在组件挂载时执行一次 useEffect(() => { if (isFirstLoad.current) { isFirstLoad.current = false; loadOperationLogs({ resetPage: true }); } }, [loadOperationLogs]); // 事件处理器 - 事件驱动模式 const handleSearch = useCallback((filters: Record) => { setSearchFilters(filters); loadOperationLogs({ filters, pagination: { page: 1, size: pagination.size } }); }, [loadOperationLogs, pagination.size]); const handlePageChange = useCallback((page: number) => { setPagination(prev => ({ ...prev, page })); loadOperationLogs({ filters: searchFilters, pagination: { page, size: pagination.size } }); }, [loadOperationLogs, searchFilters, pagination.size]); const handleSizeChange = useCallback((size: number) => { setPagination(prev => ({ ...prev, size, page: 1 })); loadOperationLogs({ filters: searchFilters, pagination: { page: 1, size } }); }, [loadOperationLogs, searchFilters]); // 计算统计数据 const stats = useMemo(() => { const totalOperations = logs.length; const successfulOperations = logs.filter(log => log.response_status >= 200 && log.response_status < 300).length; const failedOperations = totalOperations - successfulOperations; const averageProcessingTime = logs.length > 0 ? logs.reduce((sum, log) => sum + log.processing_time, 0) / logs.length : 0; return { totalOperations, successfulOperations, failedOperations, averageProcessingTime }; }, [logs]); // 查看详情 const handleViewDetail = (log: OperationLog) => { toast.info(`查看操作日志详情: ${log.username} - ${log.action}`); }; // 导出日志 const handleExport = async () => { try { setLoading(true); const exportData = await exportOperationLogs({ username: searchFilters.search, module: searchFilters.module === 'all' ? undefined : searchFilters.module, response_status: searchFilters.status === 'all' ? undefined : searchFilters.status === 'success' ? 200 : 400, }); // 创建下载链接 const dataStr = JSON.stringify(exportData, null, 2); const dataBlob = new Blob([dataStr], { type: 'application/json' }); const url = URL.createObjectURL(dataBlob); const link = document.createElement('a'); link.href = url; link.download = `operation_logs_${new Date().getTime()}.json`; link.click(); URL.revokeObjectURL(url); toast.success('导出成功'); } catch (error) { console.error('Failed to export operation logs:', error); const errorMessage = error instanceof Error ? error.message : '导出失败'; toast.error(errorMessage); } finally { setLoading(false); } }; return (
{/* 页面标题 */}

操作日志

详细追踪用户在系统中的关键操作行为

{/* 统计卡片 */}
总操作数
{stats.totalOperations}
系统总操作数
成功操作
{stats.successfulOperations}
操作成功次数
失败操作
{stats.failedOperations}
操作失败次数
平均耗时
{Math.round(stats.averageProcessingTime * 1000)}ms
操作平均耗时
{/* 搜索、表格和分页 */}
); }