Nest.js 实战 (九):使用拦截器记录用户 CURD 操作日志

news2024/9/22 7:28:01

前言

有一天,公司的产品经理提了一个需求:系统需要记录每个用户的 CURD 操作,也就是说用户新增、编辑或者删除了什么数据,都需要记录下来,这个在 Nest.js 中如何实现呢?

这时候我们可以考虑使用 拦截器 来实现。

什么是拦截器?

拦截器 是使用 @Injectable() 装饰器注解的类。拦截器应该实现 NestInterceptor 接口。

拦截器 具有一系列有用的功能,这些功能受面向切面编程(AOP)技术的启发。它们可以:

  • 在函数执行之前/之后绑定额外的逻辑
  • 转换从函数返回的结果
  • 转换从函数抛出的异常
  • 扩展基本函数行为
  • 根据所选条件完全重写函数 (例如, 缓存目的)

创建 Prisma 模型

schema.prisma 文件中添加 Log 模型:

// Log - method
enum Method {
  GET
  POST
  PATCH
  DELETE
}

// 系统管理 - 操作日志
model Log {
  id            String          @id @default(uuid()) // 主键
  userId        String          // 关联的用户 id
  user          User            @relation(fields: [userId], references: [id])
  ip            String          // ip
  action        String          // 请求地址
  method        Method          // 请求方法
  params        Json            // 请求参数(JSON 对象)
  os            String          // 系统
  browser       String          // 浏览器
  createdAt     DateTime        @default(now()) // 创建时间
}

这里可以根据自己实际需求调整。

创建 Module 模块

这里我们需要用到 Session 保存的用户数据,但 Service 中是不能直接获取 Session 的,我们需要注入作用域,以此来获取请求中的上下文。

新建 operation-log.service.ts 文件:

import { Inject, Injectable, Scope } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';
import UAParser from 'ua-parser-js';

import { PrismaService } from '@/modules/prisma/prisma.service';


@Injectable({ scope: Scope.REQUEST })
export class OperationLogService {
  constructor(
    @Inject(REQUEST)
    private readonly request: Request & { session: Api.Common.SessionInfo },
    private prisma: PrismaService,
  ) { }

  **
   * @description: 录入日志
   */
  async logAction() {
    const { originalUrl, method, headers, ip, body, query } = this.request;
    const userAgent = headers['user-agent'];
    const parser = new UAParser(userAgent);
    let { userInfo } = this.request.session;
    // 登录接口需要单独处理
    const isLogin = originalUrl === '/auth/login';
    if ((userInfo && method.toUpperCase() !== 'GET') || isLogin) {
      if (isLogin) {
        // 查询数据库中对应的用户
        userInfo = await this.prisma.user.findUnique({
          where: { userName: body.userName },
        });
      }
      const data: any = {
        userId: userInfo.id,
        action: originalUrl,
        method: method.toUpperCase(),
        ip,
        params: { ...body, ...query },
        os: Object.values(parser.getOS()).join(' '),
        browser: parser.getBrowser().name,
      };
      // 插入数据到表
      await this.prisma.log.create({
        data,
      });
    }
  }
}

因为登录接口此时 Session 还没有保存用户数据,我们需要单独处理一下,这里我们只记录非 GET 请求的路由。

创建 LoggerInterceptor 拦截器

新建 interceptor/logger.interceptor.ts 文件,写入:

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { OperationLogService } from '@/modules/system-manage/operation-log/operation-log.service';

@Injectable()
export class LoggerInterceptor implements NestInterceptor {
  constructor(private readonly operationLogService: OperationLogService) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    this.operationLogService.logAction();
    return next.handle().pipe(map((data) => data));
  }
}

绑定拦截器

在需要绑定的 Controller 中使用 @UseInterceptors() 装饰器,与守卫一样, 拦截器可以是控制器范围内的, 方法范围内的或者全局范围内的。

import { UseInterceptors } from '@nestjs/common';
import { LoggerInterceptor } from '@/interceptor/logger.interceptor';

@UseInterceptors(LoggingInterceptor)
export class UserManageController {}

在绑定拦截器后,用户每次调用 Controller 中的路由处理程序都将使用 LoggingInterceptor,也就是说会把用户的操作等信息记录到表中。

效果演示

在这里插入图片描述

总结

这个功能本来一开始我是想使用 中间件 来开发的,后来不管怎么折腾,中间件 的 Request 上下文始终获取不到 Session,但 拦截器 也不失是一种好方法。

Github 仓库:OperationLogService

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2037171.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《书生大模型实战营第3期》进阶岛 第一关: 探索 InternLM 模型能力边界

文章大纲 OpenCompass 评测体验大模型对比评测 - Bad caseBad Case 1Bad Case 2Bad Case 3Bad Case 4Bad Case 5 大模型对比评测 - Good caseGood case 1Good Case 2 任务其他学习内容参考文献本人学习系列笔记第二期第三期 课程资源论文其他参考 OpenCompass 评测体验 https:…

opencv-python图像增强七:图像亮度对比度饱和度调整

文章目录 一&#xff0c;简介二&#xff0c;图像亮度对比度修改三&#xff0c;对比度增强&#xff1a; 一&#xff0c;简介 在图像处理领域&#xff0c;对比度、亮度和饱和度是影响图像视觉效果的重要因素。合理调整这三个参数&#xff0c;可以使图像更具表现力&#xff0c;满…

北斗短报文通信原理

短报文通信原理主要基于北斗卫星导航系统&#xff0c;其过程可以分为以下几个步骤&#xff1a;用户机将包含接收方ID号和通讯内容的通讯申请信号加密后通过卫星转发入站;地面中心站接收到通讯申请信号后&#xff0c;进行脱密和再加密处理&#xff0c;然后将其加入持续广播的出站…

WindowsAPI 查阅笔记:线程、多个线程互同步

1. 线程的创建 HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程安全属性 SIZE_T dwStackSize, //线程堆栈大小 LPTHREAD_START_ROUTINE lpStartAddress, //重要: 线程函数指针 LPVOID lpParameter, //重要: 启动线程函数 DWORD dwC…

分布式知识总结(一致性Hash算法)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 一致性Hash算法 假如有三台服务器编号node0、node1、node2&…

若依项目改造-sqlserver(2)

目前启动时&#xff0c;不需要配置数据库 1、启动时&#xff0c;不用检查数据库连接 2、可以在线导出数据库备份bak文件 3、实现不用配置jdk环境启动 4、实现启动没有控制台窗口

排序【归并排序和计数排序】

1.归并排序 1.1 基本思想 并归排序&#xff1a;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子序列有序&#x…

【代理模式】设计模式系列:实现与最佳实践(掌控访问的艺术)

文章目录 Java中的代理模式引言1. 代理模式概念1.1 代理模式定义1.2 代理模式的参与者1.3 代理模式的基本工作原理1.4 代理模式的优点与缺点 2. Java代理模式实现方式2.1 静态代理2.2 动态代理2.2.1 JDK动态代理2.2.2 CGLIB动态代理 2.3 两者之间的区别与选择 3. 使用案例分析3…

Mariadb数据库本机无密码登录的问题解决

Mariadb数据库本机无密码登录的问题解决 安装了mariadb后&#xff0c;发现Mariadb本机无密码才能登录 百度了很多文章&#xff0c;发现很多人是因为root的plugin设置的值不正确导致的&#xff0c;unix_socket可以不需要密码&#xff0c;mysql_native_password 是正常的。 解…

Codeforces Round 965 (Div. 2)

前言 有人在过七夕&#xff0c;我在打 cf &#xff0c;还有某人独自一人在学校机房&#xff0c;凌晨一点骑上共享单车回宿舍欣赏沿途的秋风扫落叶。 Standings&#xff1a;2166 题目链接&#xff1a;Dashboard - Codeforces Round 965 (Div. 2) - Codeforces A. Find K Distin…

未来能源技术

未来能源技术正处于全球焦点的中心&#xff0c;旨在应对气候变化、资源枯竭和能源安全的挑战。未来能源技术的发展方向集中在可再生能源、能源储存技术、智能电网、核聚变以及新材料的应用等多个领域。 1. 可再生能源技术 1.1 太阳能技术 太阳能技术是未来能源发展的核心领域之…

精密Δ-Σ ADC的有效噪声带宽

1 简介 即使对最有经验的模拟设计工程师来说&#xff0c;理解ADC噪声也是一项挑战。Δ-Σ ADC具有量化噪声和热噪声&#xff0c;其变化取决于ADC的分辨率、参考电压和输出数据速率。在系统层面上&#xff0c;噪声分析因附加的信号链组件而变得更加复杂&#xff0c;这些组件中的…

NLP_情感分类_序列模型方案

文章目录 项目背景代码导包读取数据文本预处理举例查看分词器数据集调整进一步剖析&#xff1a;对应Step [{i1}/{len(train_loader)}] 里的train_loader进一步剖析&#xff1a;Step [{i1}/{len(train_loader)}] 里的train_loader&#xff0c;原始的train_df 计算数据集中最长文…

Java 并发(四)—— volatile 和 synchronized

一、volatile 关键字 1.概念 如果我们将一个变量使用 volatile 修饰&#xff0c;这就指示 编译器&#xff0c;这个变量是共享且不稳定的&#xff0c;每次使用它都到主存中进行读取。 2.作用 保证变量对所有线程的可见性。但不能保证数据的原子性。因此不能完全保证线程安全…

STP(生成树)的概述和工作原理

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

PHPStorm 环境配置与应用详解

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; PHPStorm 是 JetBrains 出品的一款专业 PHP 集成开发环境&#xff08;IDE&#xff09;&#xff0c;凭借其智能的代码补全、调试功能、深度框架支持和前端开发工具&#xff0c;为用户提供了丰富的功能和工具…

简单的敏感词提示功能

简单的敏感词提示功能 1. 需求 公司现在接到通知&#xff0c;部分接口的部分手动输入字段&#xff0c;需要新增敏感词报红提示&#xff0c;敏感词汇现在应该是7000多个左右&#xff0c;需要我们提供一个敏感词校验接口&#xff0c;如果前端输入敏感词&#xff0c;则前端提示出…

在Unreal Engine中使用C++创建基础角色并添加移动功能

目录 引言 步骤一&#xff1a;创建C类 步骤二&#xff1a;编写C代码 步骤三&#xff1a;设置输入绑定 步骤四&#xff1a;在UE编辑器中测试 结论 引言 Unreal Engine&#xff08;UE&#xff09;以其强大的功能和灵活性在游戏开发界广受好评。本文将指导你如何在UE中通过…

校园外卖平台小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商家管理&#xff0c;菜品信息管理&#xff0c;菜品分类管理&#xff0c;购买菜品管理&#xff0c;订单信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&a…

揭开ChatGPT进化之谜:人工智能如何变得更聪明

近年来&#xff0c;人工智能&#xff08;AI&#xff09;领域取得了显著进展&#xff0c;尤其是在自然语言处理&#xff08;NLP&#xff09;方面。OpenAI的GPT系列模型&#xff0c;如GPT-3和ChatGPT&#xff0c;代表了这一领域的前沿技术。本文将围绕ChatGPT提升的原因、发展趋势…