import { Request, Response } from 'express'; import type { LogItem, OperationModule, OperationStatus, OperationType, } from '../src/pages/LogAudit/data.d'; // 操作人姓名列表 const operatorNames = [ '张伟', '李娜', '王强', '刘洋', '陈静', '杨帆', '赵敏', '黄涛', '周丽', '吴刚', '徐静', '孙鹏', '马丽', '朱杰', '胡军', '林娜', '郭明', '何伟', '高峰', '梁雨', 'system', 'admin', 'root', ]; // 角色列表 const operatorRoles = [ '超级管理员', '系统管理员', '运营专员', '内容编辑', '普通用户', '审计员', ]; // 操作类型 const operationTypes: OperationType[] = [ 'CREATE', 'UPDATE', 'DELETE', 'QUERY', 'LOGIN', 'LOGOUT', 'EXPORT', 'IMPORT', 'ENABLE', 'DISABLE', 'OTHER', ]; // 操作模块 const operationModules: OperationModule[] = [ 'USER', 'PRODUCT', 'ORDER', 'ARTICLE', 'SYSTEM', 'AUTH', 'OTHER', ]; // 操作描述模板 const operationDescMap: Record< OperationModule, Record > = { USER: { CREATE: ['创建新用户账号', '批量导入用户数据', '注册新会员'], UPDATE: ['修改用户信息', '更新用户头像', '修改用户邮箱'], DELETE: ['删除用户账号', '批量删除用户', '注销用户'], QUERY: ['查询用户列表', '查看用户详情', '搜索用户'], LOGIN: ['用户登录系统', '扫码登录', '短信验证码登录'], LOGOUT: ['用户退出登录', '会话超时退出', '强制退出'], EXPORT: ['导出用户列表', '导出用户报表'], IMPORT: ['导入用户数据', '批量导入用户'], ENABLE: ['启用用户账号', '激活用户'], DISABLE: ['禁用用户账号', '冻结用户'], OTHER: ['重置用户密码', '发送验证邮件'], }, PRODUCT: { CREATE: ['创建商品', '新增商品信息', '批量添加商品'], UPDATE: ['修改商品信息', '更新商品价格', '修改商品库存'], DELETE: ['删除商品', '批量删除商品'], QUERY: ['查询商品列表', '查看商品详情', '搜索商品'], LOGIN: [], LOGOUT: [], EXPORT: ['导出商品列表', '导出商品库存报表'], IMPORT: ['导入商品数据', '批量导入商品信息'], ENABLE: ['上架商品', '启用商品'], DISABLE: ['下架商品', '禁用商品'], OTHER: ['修改商品图片', '更新商品分类'], }, ORDER: { CREATE: ['创建订单', '生成新订单'], UPDATE: ['修改订单状态', '更新订单信息', '修改收货地址'], DELETE: ['删除订单', '取消订单'], QUERY: ['查询订单列表', '查看订单详情', '搜索订单'], LOGIN: [], LOGOUT: [], EXPORT: ['导出订单列表', '导出销售报表'], IMPORT: ['导入订单数据'], ENABLE: [], DISABLE: [], OTHER: ['订单退款', '订单发货', '订单备注'], }, ARTICLE: { CREATE: ['发布文章', '创建新文章', '保存草稿'], UPDATE: ['修改文章内容', '更新文章标题', '编辑文章'], DELETE: ['删除文章', '批量删除文章'], QUERY: ['查询文章列表', '查看文章详情', '搜索文章'], LOGIN: [], LOGOUT: [], EXPORT: ['导出文章内容'], IMPORT: ['导入文章'], ENABLE: ['发布文章', '启用文章'], DISABLE: ['隐藏文章', '禁用文章'], OTHER: ['文章审核', '文章置顶'], }, SYSTEM: { CREATE: ['创建系统配置', '添加系统参数'], UPDATE: ['修改系统配置', '更新系统参数'], DELETE: ['删除系统配置', '清除缓存'], QUERY: ['查询系统日志', '查看系统状态'], LOGIN: [], LOGOUT: [], EXPORT: ['导出系统日志', '导出配置备份'], IMPORT: ['导入系统配置'], ENABLE: ['启用功能模块'], DISABLE: ['禁用功能模块'], OTHER: ['系统备份', '系统恢复', '清理日志'], }, AUTH: { CREATE: ['创建角色', '添加权限', '新建菜单'], UPDATE: ['修改角色权限', '更新菜单', '编辑权限'], DELETE: ['删除角色', '移除权限', '删除菜单'], QUERY: ['查询角色列表', '查看权限树'], LOGIN: ['用户认证', '令牌刷新'], LOGOUT: ['令牌失效'], EXPORT: ['导出权限配置'], IMPORT: ['导入权限配置'], ENABLE: ['启用角色'], DISABLE: ['禁用角色'], OTHER: ['密码修改', '权限分配'], }, OTHER: { CREATE: ['创建数据'], UPDATE: ['更新数据'], DELETE: ['删除数据'], QUERY: ['查询数据'], LOGIN: [], LOGOUT: [], EXPORT: ['导出数据'], IMPORT: ['导入数据'], ENABLE: ['启用功能'], DISABLE: ['禁用功能'], OTHER: ['其他操作'], }, }; // 请求方法 const requestMethods: Array<'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'> = [ 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', ]; // 请求 URL 模板 const urlTemplates: Record = { USER: [ '/api/users', '/api/users/list', '/api/users/detail', '/api/auth/login', '/api/auth/logout', ], PRODUCT: [ '/api/products', '/api/products/list', '/api/products/detail', '/api/products/stock', ], ORDER: [ '/api/orders', '/api/orders/list', '/api/orders/detail', '/api/orders/status', ], ARTICLE: [ '/api/articles', '/api/articles/list', '/api/articles/detail', '/api/articles/publish', ], SYSTEM: [ '/api/system/config', '/api/system/logs', '/api/system/status', '/api/system/backup', ], AUTH: [ '/api/auth/roles', '/api/auth/permissions', '/api/auth/menus', '/api/auth/login', ], OTHER: ['/api/misc', '/api/utils', '/api/common'], }; // IP 地址池 const ipPrefixes = [ '192.168.1', '192.168.0', '10.0.0', '172.16.0', '113.45', '36.110', '223.5.5', ]; // User Agent 列表 const userAgents = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/120.0.0.0 Safari/537.36', 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Mobile/15E148 Safari/604.1', 'Mozilla/5.0 (Linux; Android 13; SM-G981B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', ]; // 错误信息模板 const errorMessages = [ '参数校验失败:必填字段为空', '权限不足:当前用户无操作权限', '数据不存在:记录已被删除或不存在', '数据库连接超时', '系统繁忙,请稍后再试', '请求参数格式错误', '接口限流,请求过于频繁', 'Token 已过期,请重新登录', '密码错误,请重新输入', '账号已被锁定', ]; // 生成随机整数 function randomInt(min: number, max: number): number { return Math.floor(Math.random() * (max - min + 1)) + min; } // 生成随机日期(近30天) function randomDate(): string { const now = new Date(); const daysAgo = randomInt(0, 30); const hoursAgo = randomInt(0, 23); const minutesAgo = randomInt(0, 59); const secondsAgo = randomInt(0, 59); const date = new Date( now.getTime() - daysAgo * 24 * 60 * 60 * 1000 - hoursAgo * 60 * 60 * 1000 - minutesAgo * 60 * 1000 - secondsAgo * 1000, ); return date.toISOString(); } // 生成随机 IP function randomIP(): string { const prefix = ipPrefixes[randomInt(0, ipPrefixes.length - 1)]; return `${prefix}.${randomInt(1, 254)}`; } // 生成单条日志数据 function generateLog(id: number): LogItem { const module = operationModules[randomInt(0, operationModules.length - 1)]; const type = operationTypes[randomInt(0, operationTypes.length - 1)]; const descList = operationDescMap[module][type]; const desc = descList.length > 0 ? descList[randomInt(0, descList.length - 1)] : `${type}操作`; // 90% 成功,10% 失败 const isSuccess = Math.random() > 0.1; const status: OperationStatus = isSuccess ? 'success' : 'failure'; const operatorName = operatorNames[randomInt(0, operatorNames.length - 1)]; const operatorId = operatorName === 'system' ? '0' : randomInt(1000, 9999).toString(); return { id: id.toString(), operatorId, operatorName, operatorRole: operatorRoles[randomInt(0, operatorRoles.length - 1)], operationType: type, operationModule: module, operationDesc: desc, requestMethod: requestMethods[randomInt(0, requestMethods.length - 1)], requestUrl: urlTemplates[module][randomInt(0, urlTemplates[module].length - 1)], requestParams: Math.random() > 0.5 ? JSON.stringify({ id: randomInt(1, 1000), page: 1, size: 20 }) : undefined, responseData: isSuccess && Math.random() > 0.7 ? JSON.stringify({ code: 200, message: 'success', data: {} }) : undefined, ipAddress: randomIP(), userAgent: Math.random() > 0.3 ? userAgents[randomInt(0, userAgents.length - 1)] : undefined, status, errorMessage: !isSuccess ? errorMessages[randomInt(0, errorMessages.length - 1)] : undefined, executionTime: randomInt(10, 5000), createdAt: randomDate(), }; } // 生成 150 条日志数据 const logs: LogItem[] = Array.from({ length: 150 }, (_, i) => generateLog(i + 1), ); // 按时间倒序排序 logs.sort( (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), ); export default { // 获取日志列表 GET /api/logs 'GET /api/logs': (req: Request, res: Response) => { const { current = 1, pageSize = 20, operatorName, operationType, operationModule, status, startTime, endTime, ipAddress, sortField = 'createdAt', sortOrder = 'descend', } = req.query; let filteredData = [...logs]; // 操作人姓名筛选 if (operatorName) { filteredData = filteredData.filter((item) => item.operatorName.includes(operatorName as string), ); } // 操作类型筛选 if (operationType) { filteredData = filteredData.filter( (item) => item.operationType === operationType, ); } // 操作模块筛选 if (operationModule) { filteredData = filteredData.filter( (item) => item.operationModule === operationModule, ); } // 状态筛选 if (status) { filteredData = filteredData.filter((item) => item.status === status); } // 时间范围筛选 if (startTime) { const start = new Date(startTime as string).getTime(); filteredData = filteredData.filter( (item) => new Date(item.createdAt).getTime() >= start, ); } if (endTime) { const end = new Date(endTime as string).getTime(); filteredData = filteredData.filter( (item) => new Date(item.createdAt).getTime() <= end, ); } // IP 地址筛选 if (ipAddress) { filteredData = filteredData.filter((item) => item.ipAddress.includes(ipAddress as string), ); } // 排序 if (sortField && sortOrder) { filteredData.sort((a, b) => { const aValue = a[sortField as keyof LogItem]; const bValue = b[sortField as keyof LogItem]; const isAscend = sortOrder === 'ascend'; if (typeof aValue === 'string' && typeof bValue === 'string') { return isAscend ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue); } if (typeof aValue === 'number' && typeof bValue === 'number') { return isAscend ? aValue - bValue : bValue - aValue; } return 0; }); } // 分页 const currentNum = parseInt(current as string, 10); const pageSizeNum = parseInt(pageSize as string, 10); const startIndex = (currentNum - 1) * pageSizeNum; const endIndex = startIndex + pageSizeNum; const pagedData = filteredData.slice(startIndex, endIndex); // 模拟网络延迟 setTimeout(() => { res.json({ data: pagedData, total: filteredData.length, success: true, }); }, 300); }, // 获取日志详情 GET /api/logs/:id 'GET /api/logs/:id': (req: Request, res: Response) => { const log = logs.find((item) => item.id === req.params.id); if (!log) { res.status(404).json({ success: false, message: '日志记录不存在', }); return; } res.json({ data: log, success: true, }); }, // 导出日志 POST /api/logs/export 'POST /api/logs/export': (req: Request, res: Response) => { const { operatorName, operationType, operationModule, status, startTime, endTime, ipAddress, } = req.body; let filteredData = [...logs]; // 应用相同的筛选逻辑 if (operatorName) { filteredData = filteredData.filter((item) => item.operatorName.includes(operatorName), ); } if (operationType) { filteredData = filteredData.filter( (item) => item.operationType === operationType, ); } if (operationModule) { filteredData = filteredData.filter( (item) => item.operationModule === operationModule, ); } if (status) { filteredData = filteredData.filter((item) => item.status === status); } if (startTime) { const start = new Date(startTime).getTime(); filteredData = filteredData.filter( (item) => new Date(item.createdAt).getTime() >= start, ); } if (endTime) { const end = new Date(endTime).getTime(); filteredData = filteredData.filter( (item) => new Date(item.createdAt).getTime() <= end, ); } if (ipAddress) { filteredData = filteredData.filter((item) => item.ipAddress.includes(ipAddress), ); } // 生成 CSV 内容 const headers = [ 'ID', '操作人', '操作类型', '操作模块', '操作描述', '请求方法', '请求URL', 'IP地址', '状态', '执行时间(ms)', '操作时间', ]; const rows = filteredData.map((log) => [ log.id, log.operatorName, log.operationType, log.operationModule, log.operationDesc, log.requestMethod, log.requestUrl, log.ipAddress, log.status, log.executionTime, new Date(log.createdAt).toLocaleString('zh-CN'), ]); const csvContent = [ headers.join(','), ...rows.map((row) => row.map((cell) => `"${cell}"`).join(',')), ].join('\n'); // 设置响应头,触发浏览器下载 res.setHeader('Content-Type', 'text/csv; charset=utf-8'); res.setHeader( 'Content-Disposition', `attachment; filename=logs_export_${new Date() .toISOString() .slice(0, 10)}.csv`, ); // 添加 BOM 以支持中文显示 const bom = '\uFEFF'; res.send(bom + csvContent); }, };