feat: 支持审计日志,操作日志

This commit is contained in:
xiaojunnuo
2026-07-12 02:29:54 +08:00
parent 4250d0e266
commit f2855d6dac
83 changed files with 1252 additions and 1341 deletions
@@ -1,108 +1,66 @@
import { BaseService } from "@certd/lib-server";
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";
import { InjectEntityModel } from "@midwayjs/typeorm";
import { LessThan, Repository } from "typeorm";
import { AuditLogEntity } from "../entity/audit-log.js";
import { logger } from "@certd/basic";
import { Cron } from "../../../cron/cron.js";
export type AuditPageReq = {
page?: { offset: number; limit: number };
query?: {
userId?: number;
type?: string;
action?: string;
createTime_start?: string;
createTime_end?: string;
};
sort?: { prop: string; asc: boolean };
};
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class AuditService {
@Inject()
dataSourceManager: TypeORMDataSourceManager;
export class AuditService extends BaseService<AuditLogEntity> {
@InjectEntityModel(AuditLogEntity)
repository: Repository<AuditLogEntity>;
@Inject()
cron: Cron;
getRepository(): Repository<AuditLogEntity> {
return this.dataSourceManager.getDataSource("default").getRepository(AuditLogEntity);
getRepository() {
return this.repository;
}
async log(params: { userId: number; type: string; action: string; content: string; username?: string; projectId?: number; projectName?: string; ipAddress?: string }): Promise<void> {
async page(pageReq: any) {
if (pageReq.query?.createTime) {
const start = pageReq.query.createTime[0];
const end = pageReq.query.createTime[1];
delete pageReq.query.createTime;
pageReq.buildQuery = (qb: any) => {
qb.andWhere("main.createTime BETWEEN :start AND :end", { start, end });
};
}
return await super.page(pageReq);
}
async log(params: { userId: number; type: string; action: string; content: string; username?: string; projectId?: number; ipAddress?: string; scope?: string }): Promise<void> {
try {
let { username } = params;
if (!username && params.userId != null) {
const userRepo = this.dataSourceManager.getDataSource("default").getRepository("sys_user" as any);
const userRepo = this.repository.manager.connection.getRepository("sys_user" as any);
const user = await userRepo.findOne({ where: { id: params.userId } });
username = user?.username || "";
}
const repo = this.getRepository();
const entity = new AuditLogEntity();
entity.userId = params.userId;
entity.userName = username || "";
entity.username = username || "";
entity.type = params.type;
entity.action = params.action;
entity.content = params.content;
entity.projectId = params.projectId || 0;
entity.projectName = params.projectName || "";
entity.projectName = "";
entity.ipAddress = params.ipAddress || "";
await repo.save(entity);
entity.scope = params.scope || "user";
await this.repository.save(entity);
} catch (e) {
logger.error("写入审计日志失败:", e);
}
}
async page(pageReq: AuditPageReq) {
const repo = this.getRepository();
const { page, query, sort } = pageReq;
const offset = page?.offset ?? 0;
const limit = page?.limit ?? 20;
const qb = repo.createQueryBuilder("main");
if (query?.userId != null) {
qb.andWhere("main.userId = :userId", { userId: query.userId });
}
if (query?.type) {
qb.andWhere("main.type = :type", { type: query.type });
}
if (query?.action) {
qb.andWhere("main.action = :action", { action: query.action });
}
if (query?.createTime_start) {
qb.andWhere("main.createTime >= :start", { start: query.createTime_start });
}
if (query?.createTime_end) {
qb.andWhere("main.createTime <= :end", { end: query.createTime_end });
}
if (sort?.prop) {
qb.orderBy("main." + sort.prop, sort.asc ? "ASC" : "DESC");
} else {
qb.orderBy("main.id", "DESC");
}
qb.skip(offset).take(limit);
const list = await qb.getMany();
const total = await qb.getCount();
return {
records: list,
total,
offset,
limit,
};
}
async cleanExpired(retentionDays: number): Promise<number> {
const repo = this.getRepository();
const cutoff = new Date();
cutoff.setDate(cutoff.getDate() - retentionDays);
const result = await repo.delete({
const result = await this.repository.delete({
createTime: LessThan(cutoff),
} as any);
const deletedCount = result.affected || 0;
@@ -112,11 +70,6 @@ export class AuditService {
return deletedCount;
}
async delete(id: number): Promise<void> {
const repo = this.getRepository();
await repo.delete({ id });
}
registerCleanCron() {
this.cron.register({
name: "audit.cleanExpired",