最近因業務需求,系統須要引入操做日誌. 當用戶修改表數據的時候留下操做記錄,以便往後查看及維護. 你們都知道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('測試')
}
}
複製代碼