/** * 操作日志路由 * @file operationLogs.js * @description 定义操作日志相关的API路由 */ const express = require('express'); const router = express.Router(); const { body, query, param, validationResult } = require('express-validator'); const operationLogController = require('../controllers/operationLogController'); const { verifyToken } = require('../middleware/auth'); const { checkOperationLogPermission } = require('../middleware/operationLogAuth'); // 验证中间件 const validateRequest = (req, res, next) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ success: false, message: '请求参数验证失败', errors: errors.array() }); } next(); }; /** * @swagger * components: * schemas: * OperationLog: * type: object * properties: * id: * type: integer * description: 主键ID * user_id: * type: integer * description: 操作用户ID * username: * type: string * description: 操作用户名 * user_role: * type: string * description: 操作用户角色 * operation_type: * type: string * enum: [CREATE, UPDATE, DELETE] * description: 操作类型 * module_name: * type: string * description: 操作模块名称 * table_name: * type: string * description: 操作的数据表名 * record_id: * type: integer * description: 操作的记录ID * operation_desc: * type: string * description: 操作描述 * old_data: * type: object * description: 操作前的数据 * new_data: * type: object * description: 操作后的数据 * ip_address: * type: string * description: 操作IP地址 * user_agent: * type: string * description: 用户代理信息 * request_url: * type: string * description: 请求URL * request_method: * type: string * description: 请求方法 * response_status: * type: integer * description: 响应状态码 * execution_time: * type: integer * description: 执行时间(毫秒) * error_message: * type: string * description: 错误信息 * created_at: * type: string * format: date-time * description: 创建时间 */ /** * @swagger * /api/operation-logs: * get: * summary: 获取操作日志列表 * tags: [OperationLogs] * security: * - bearerAuth: [] * parameters: * - in: query * name: page * schema: * type: integer * default: 1 * description: 页码 * - in: query * name: pageSize * schema: * type: integer * default: 20 * description: 每页记录数 * - in: query * name: userId * schema: * type: integer * description: 用户ID * - in: query * name: username * schema: * type: string * description: 用户名 * - in: query * name: operationType * schema: * type: string * enum: [CREATE, UPDATE, DELETE] * description: 操作类型 * - in: query * name: moduleName * schema: * type: string * description: 模块名称 * - in: query * name: tableName * schema: * type: string * description: 数据表名 * - in: query * name: startDate * schema: * type: string * format: date * description: 开始日期 * - in: query * name: endDate * schema: * type: string * format: date * description: 结束日期 * - in: query * name: sortBy * schema: * type: string * default: created_at * description: 排序字段 * - in: query * name: sortOrder * schema: * type: string * enum: [ASC, DESC] * default: DESC * description: 排序方向 * responses: * 200: * description: 获取操作日志列表成功 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * data: * type: array * items: * $ref: '#/components/schemas/OperationLog' * pagination: * type: object * properties: * total: * type: integer * page: * type: integer * pageSize: * type: integer * totalPages: * type: integer * hasMore: * type: boolean * message: * type: string * 500: * description: 服务器内部错误 */ router.get('/', verifyToken, checkOperationLogPermission, query('page').optional().isInt({ min: 1 }).withMessage('页码必须是正整数'), query('pageSize').optional().isInt({ min: 1, max: 100 }).withMessage('每页记录数必须是1-100之间的整数'), query('username').optional().isLength({ min: 0, max: 50 }).withMessage('用户名长度必须在0-50个字符之间'), query('operationType').optional().isIn(['CREATE', 'UPDATE', 'DELETE']).withMessage('操作类型必须是CREATE、UPDATE或DELETE'), query('moduleName').optional().isLength({ min: 1, max: 100 }).withMessage('模块名称长度必须在1-100个字符之间'), query('tableName').optional().isLength({ min: 1, max: 100 }).withMessage('数据表名长度必须在1-100个字符之间'), query('startDate').optional().isISO8601().withMessage('开始日期格式不正确'), query('endDate').optional().isISO8601().withMessage('结束日期格式不正确'), query('sortBy').optional().isIn(['id', 'user_id', 'username', 'operation_type', 'module_name', 'table_name', 'created_at']).withMessage('排序字段无效'), query('sortOrder').optional().isIn(['ASC', 'DESC']).withMessage('排序方向必须是ASC或DESC'), validateRequest, operationLogController.getOperationLogs ); /** * @swagger * /api/operation-logs/{id}: * get: * summary: 获取操作日志详情 * tags: [OperationLogs] * security: * - bearerAuth: [] * parameters: * - in: path * name: id * required: true * schema: * type: integer * description: 操作日志ID * responses: * 200: * description: 获取操作日志详情成功 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * data: * $ref: '#/components/schemas/OperationLog' * message: * type: string * 404: * description: 操作日志不存在 * 500: * description: 服务器内部错误 */ router.get('/export', verifyToken, checkOperationLogPermission, operationLogController.exportOperationLogs ); router.get('/:id', verifyToken, checkOperationLogPermission, operationLogController.getOperationLogById ); /** * @swagger * /api/operation-logs/stats/overview: * get: * summary: 获取操作统计 * tags: [OperationLogs] * security: * - bearerAuth: [] * parameters: * - in: query * name: type * schema: * type: string * enum: [user, module, overall] * default: overall * description: 统计类型 * - in: query * name: userId * schema: * type: integer * description: 用户ID(type为user时必填) * - in: query * name: moduleName * schema: * type: string * description: 模块名称(type为module时必填) * - in: query * name: startDate * schema: * type: string * format: date * description: 开始日期 * - in: query * name: endDate * schema: * type: string * format: date * description: 结束日期 * responses: * 200: * description: 获取操作统计成功 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * data: * type: object * properties: * CREATE: * type: integer * UPDATE: * type: integer * DELETE: * type: integer * message: * type: string * 500: * description: 服务器内部错误 */ router.get('/stats/overview', verifyToken, checkOperationLogPermission, operationLogController.getOperationStats ); /** * @swagger * /api/operation-logs/stats/chart: * get: * summary: 获取操作日志图表数据 * tags: [OperationLogs] * security: * - bearerAuth: [] * parameters: * - in: query * name: type * schema: * type: string * enum: [hourly, daily, monthly] * default: daily * description: 图表类型 * - in: query * name: startDate * schema: * type: string * format: date * description: 开始日期 * - in: query * name: endDate * schema: * type: string * format: date * description: 结束日期 * responses: * 200: * description: 获取操作日志图表数据成功 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * data: * type: object * properties: * dates: * type: array * items: * type: string * series: * type: object * properties: * CREATE: * type: array * items: * type: integer * UPDATE: * type: array * items: * type: integer * DELETE: * type: array * items: * type: integer * message: * type: string * 500: * description: 服务器内部错误 */ router.get('/stats/chart', verifyToken, checkOperationLogPermission, operationLogController.getOperationChartData ); /** * @swagger * /api/operation-logs/clean: * post: * summary: 清理过期日志 * tags: [OperationLogs] * security: * - bearerAuth: [] * requestBody: * required: false * content: * application/json: * schema: * type: object * properties: * daysToKeep: * type: integer * default: 90 * description: 保留天数 * responses: * 200: * description: 清理过期日志成功 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * data: * type: object * properties: * deletedCount: * type: integer * message: * type: string * 500: * description: 服务器内部错误 */ router.post('/clean', verifyToken, checkOperationLogPermission, operationLogController.cleanExpiredLogs ); module.exports = router;