Files
smart_grid_iot/server/src/middleware/log.middleware.ts
T
andyesfly 1c83e09fa1 feat: 完成客户拜访模块全功能开发
本次提交实现了完整的客户拜访业务系统:
1.  新增对象存储上传服务,支持S3兼容协议的RustFS存储
2.  完善客户拜访实体与数据库表结构,补充全业务字段
3.  开发拜访台账与记录的前后端接口,包含增删改查、批量创建、状态流转等能力
4.  新增拜访表单页面与路由,支持父级菜单补全面包屑
5.  新增勘察列表页预填客户ID跳转逻辑
6.  补充UI设计文档与页面截图
7.  调整日志中间件跳过上传路由,优化RBAC权限配置
2026-09-14 11:42:10 +08:00

112 lines
3.1 KiB
TypeScript

import { Middleware, IMiddleware } from '@midwayjs/core';
import { Context, NextFunction } from '@midwayjs/koa';
import { Provide, Inject } from '@midwayjs/core';
import { InjectRepository } from '@midwayjs/sequelize';
import SysOperationLog from '../entity/sysOperationLog.entity';
@Provide()
export class LogMiddleware implements IMiddleware<Context, NextFunction> {
// 中间件匹配范围:全局生效,跳过日志查询、健康检查、登录(登录由内部记录)与文件上传(multipart 无记录价值)
ignore = ['/api/logs/*', '/api/health', '/api/auth/login', '/api/upload/*'];
@InjectRepository(SysOperationLog)
logModel!: typeof SysOperationLog;
resolve() {
return async (ctx: Context, next: NextFunction) => {
await next();
// 仅记录写操作,且要求登录用户存在
if (!['POST', 'PUT', 'DELETE'].includes(ctx.method)) {
return;
}
if (!ctx.state.user) {
return;
}
const logData = {
userId: ctx.state.user.userId,
module: this.getModuleFromUrl(ctx.url),
action: this.getActionDescription(ctx),
method: ctx.method,
params: JSON.stringify(ctx.request.body || {}),
ip: ctx.ip,
};
this.saveLog(logData).catch(err => {
console.error('保存操作日志失败:', err);
});
};
}
private getActionDescription(ctx: Context): string {
const actionMap: Record<string, Record<string, string>> = {
'/api/auth/login': { POST: '用户登录' },
'/api/auth/logout': { POST: '用户登出' },
'/api/users': {
POST: '创建用户',
PUT: '更新用户',
DELETE: '删除用户',
},
'/api/roles': {
POST: '创建角色',
PUT: '更新角色',
DELETE: '删除角色',
},
'/api/menus': {
POST: '创建菜单',
PUT: '更新菜单',
DELETE: '删除菜单',
},
'/api/dicts': {
POST: '创建字典',
PUT: '更新字典',
DELETE: '删除字典',
},
};
const basePath = ctx.url.split('?')[0];
if (actionMap[basePath]?.[ctx.method]) {
return actionMap[basePath][ctx.method];
}
for (const [path, actions] of Object.entries(actionMap)) {
if (basePath.startsWith(path) && actions[ctx.method]) {
return actions[ctx.method];
}
}
return `${ctx.method} ${basePath}`;
}
private getModuleFromUrl(url: string): string {
const moduleMap: Record<string, string> = {
'/api/auth': '认证',
'/api/users': '用户管理',
'/api/roles': '角色管理',
'/api/menus': '菜单管理',
'/api/dicts': '数据字典',
'/api/logs': '日志管理',
'/api/dashboard': '仪表盘',
};
for (const [path, module] of Object.entries(moduleMap)) {
if (url.startsWith(path)) {
return module;
}
}
return '其他';
}
private async saveLog(logData: any): Promise<void> {
try {
await this.logModel.create(logData);
} catch (error) {
console.error('写入操作日志失败:', error);
}
}
}