最近因业务需求,系统须要引入操做日志. 当用户修改表数据的时候留下操做记录,以便往后查看及维护. 你们都知道Spring AOP实现起来很方便. 其实Nest也是能够实现的. 下面是本身写的一套方法.typescript
字段 | 解释 |
---|---|
operator | 操做者 |
method | 调用的方法 |
operation | 方法描述 |
entity | 操做的实体 |
entityId | 实体ID |
oldEntity | 操做前的数据 |
newEntity | 操做后的数据 |
主要是利用typeorm中的subscriber监听数据库的变更.数据库
operation.entity.ts
import { Column, Entity } from 'typeorm';
@Entity('operationLog')
export class OperationLog {
@Column('varchar', { comment: '操做人' })
operator: string;
@Column('varchar', { comment: '调用的方法' })
method: string;
@Column('varchar', { comment: '操做名称' })
operation: string;
@Column('varchar', { comment: '数据库表名' })
entity: string;
@Column('varchar')
entityId: string;
@Column('json')
oldEntity: Record<string, any>;
@Column('json')
newEntity: Record<string, any>;
}
复制代码
operation.service.ts
import { InjectRepository } from '@nestjs/typeorm';
import { TypeOrmCrudService } from '@nestjsx/crud-typeorm';
import { OperationLog } from './entities/operation-log.entity';
import { ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';
@Injectable()
export class OperationLogService extends TypeOrmCrudService<OperationLog> {
public context: ExecutionContext;
constructor(private readonly reflector: Reflector, @InjectRepository(OperationLog) repo) {
super(repo);
}
async save<T extends { id: string | number }>(event: UpdateEvent<T> & RemoveEvent<T> & InsertEvent<T>) {
const handler = this.context.getHandler();
const operator = this.context.switchToHttp().getRequest().user.username; //从request上得到user信息
const operation = this.reflector.get('operation', handler); //得到方法上的注解
const { entity, databaseEntity } = event;
const data = {
operator,
oldEntity: databaseEntity,
newEntity: entity,
method: handler.name,
operation: operation,
entityId: String(entity.id),
entity: event.metadata.tableName,
};
//判断是否有更新及是否须要记录日志
if (event.updatedColumns.length > 0 && operation) {
await this.repo.save(data);
}
}
}
复制代码
import { Module } from '@nestjs/common';
import { OperationLogService } from './operation-log.service';
import { OperationLogController } from './operation-log.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { OperationLog } from './entities/operation-log.entity';
@Module({
controllers: [OperationLogController],
providers: [OperationLogService],
imports: [TypeOrmModule.forFeature([OperationLog])],
exports: [OperationLogService],
})
export class OperationLogModule {}
复制代码
operation.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const OperationLog = (operation:string) => SetMetadata('operation-log', operation);
复制代码
import { OperationLogModule } from './modules/operation-log/operation-log.module';
@Module({
imports: [
OperationLogModule, //只有在主模块中引用,拦截器中调用的方法才会是一个单例.
],
})
export class AppModule {}
复制代码
operation.intecepotr.ts
import { CallHandler, ExecutionContext, Inject, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { OperationLogService } from '../modules/operation-log/operation-log.service';
@Injectable()
export class OperationLogInterceptor implements NestInterceptor {
constructor(@Inject(OperationLogService) private service: OperationLogService) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
this.service.context = context; //把context赋值到service上
return next.handle().pipe(
map(data => {
return data;
}),
);
}
}
复制代码
import { Connection, EntitySubscriberInterface, getConnection, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';
import { Inject, Injectable } from '@nestjs/common';
import { OperationLogService } from '../../operation-log/operation-log.service';
@Injectable()
export class ChannelSubscriber implements EntitySubscriberInterface<Channel> {
// 不能注入REQUEST,只能靠单例的service收集Request而后注入到subscriber中
constructor(connection: Connection, @Inject(OperationLogService) private service: OperationLogService) {
connection.subscribers.push(this);
}
//数据更新成功后悔执行的方法.
async afterUpdate(event: UpdateEvent<Channel>) {
await this.service.save<Channel>(event);
}
}
复制代码
import { Controller, UseInterceptors } from "@nestjs/common";
import { ApiTags } from '@nestjs/swagger';
import { UserService } from './user.service';
import { OperationLog } from "../../decorators/operation-log.decorator";
import { OperationLogInterceptor } from "../../interceptors/operation-log.interceptor";
@ApiTags('user')
@Controller('user')
@UseInterceptors(OperationLogInterceptor) //这里可变成全局拦截器
export class UserController {
@OperationLog('测试')
test(){
console.log('测试')
}
}
复制代码