302 lines
13 KiB
TypeScript
Executable File
302 lines
13 KiB
TypeScript
Executable File
import { Injectable } from "@nestjs/common";
|
|
import { TrpcService } from "@server/trpc/trpc.service";
|
|
import { SystemLogService } from "./systemLog.service";
|
|
import { z, ZodType } from "zod";
|
|
import { Prisma } from "@nice/common";
|
|
|
|
// 定义Zod类型Schema
|
|
const SystemLogCreateArgsSchema: ZodType<Prisma.SystemLogCreateArgs> = z.any();
|
|
const SystemLogFindManyArgsSchema: ZodType<Prisma.SystemLogFindManyArgs> = z.any();
|
|
const SystemLogFindUniqueArgsSchema: ZodType<Prisma.SystemLogFindUniqueArgs> = z.any();
|
|
const SystemLogWhereInputSchema: ZodType<Prisma.SystemLogWhereInput> = z.any();
|
|
const SystemLogSelectSchema: ZodType<Prisma.SystemLogSelect> = z.any();
|
|
|
|
@Injectable()
|
|
export class SystemLogRouter {
|
|
constructor(
|
|
private readonly trpc: TrpcService,
|
|
private readonly systemLogService: SystemLogService,
|
|
) { }
|
|
|
|
router = this.trpc.router({
|
|
// 创建日志
|
|
create: this.trpc.procedure
|
|
.input(z.object({
|
|
level: z.enum(['info', 'warning', 'error', 'debug']).default('info'),
|
|
module: z.string(),
|
|
action: z.string(),
|
|
operatorId: z.string().optional(),
|
|
ipAddress: z.string().optional(),
|
|
targetId: z.string().optional(),
|
|
targetType: z.string().optional(),
|
|
targetName: z.string().optional(),
|
|
message: z.string().optional(),
|
|
details: z.any().optional(),
|
|
beforeData: z.any().optional(),
|
|
afterData: z.any().optional(),
|
|
status: z.enum(['success', 'failure']).default('success'),
|
|
errorMessage: z.string().optional(),
|
|
departmentId: z.string().optional(),
|
|
}))
|
|
.mutation(async ({ input, ctx }) => {
|
|
const ctxIpAddress = ctx.ip;
|
|
const operatorId = ctx.staff?.id;
|
|
|
|
try {
|
|
return this.systemLogService.create({
|
|
data: {
|
|
level: input.level,
|
|
module: input.module,
|
|
action: input.action,
|
|
operatorId: input.operatorId || operatorId,
|
|
ipAddress: input.ipAddress || ctxIpAddress,
|
|
targetId: input.targetId,
|
|
targetType: input.targetType,
|
|
targetName: input.targetName,
|
|
message: input.message,
|
|
details: input.details,
|
|
beforeData: input.beforeData,
|
|
afterData: input.afterData,
|
|
status: input.status,
|
|
errorMessage: input.errorMessage,
|
|
departmentId: input.departmentId,
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('Error creating system log:', error);
|
|
throw new Error('Failed to create system log');
|
|
}
|
|
}),
|
|
|
|
// 查询日志列表
|
|
findMany: this.trpc.procedure
|
|
.input(SystemLogFindManyArgsSchema)
|
|
.query(async ({ input }) => {
|
|
return this.systemLogService.findMany(input);
|
|
}),
|
|
|
|
// 查询日志列表(带分页) - 保留原名
|
|
getLogs: this.trpc.procedure
|
|
.input(z.object({
|
|
page: z.number().default(1),
|
|
pageSize: z.number().default(20),
|
|
where: SystemLogWhereInputSchema.optional(),
|
|
select: SystemLogSelectSchema.optional(),
|
|
}))
|
|
.query(async ({ input }) => {
|
|
try {
|
|
const { page, pageSize, where = {}, select } = input;
|
|
|
|
return await this.systemLogService.findManyWithPagination({
|
|
page,
|
|
pageSize,
|
|
where,
|
|
...(select ? { select } : {})
|
|
});
|
|
} catch (error) {
|
|
console.error('Error in getLogs:', error);
|
|
// 返回空结果,避免崩溃
|
|
return {
|
|
items: [],
|
|
total: 0,
|
|
page: input.page,
|
|
pageSize: input.pageSize,
|
|
totalPages: 0
|
|
};
|
|
}
|
|
}),
|
|
|
|
// 查询日志列表(带分页) - 新名称
|
|
findManyWithPagination: this.trpc.procedure
|
|
.input(z.object({
|
|
page: z.number().default(1),
|
|
pageSize: z.number().default(20),
|
|
where: SystemLogWhereInputSchema.optional(),
|
|
select: SystemLogSelectSchema.optional(),
|
|
}))
|
|
.query(async ({ input }) => {
|
|
try {
|
|
const { page, pageSize, where = {}, select } = input;
|
|
|
|
return await this.systemLogService.findManyWithPagination({
|
|
page,
|
|
pageSize,
|
|
where,
|
|
...(select ? { select } : {})
|
|
});
|
|
} catch (error) {
|
|
console.error('Error in findManyWithPagination:', error);
|
|
// 返回空结果,避免崩溃
|
|
return {
|
|
items: [],
|
|
total: 0,
|
|
page: input.page,
|
|
pageSize: input.pageSize,
|
|
totalPages: 0
|
|
};
|
|
}
|
|
}),
|
|
|
|
// 获取单个日志详情
|
|
findUnique: this.trpc.procedure
|
|
.input(SystemLogFindUniqueArgsSchema)
|
|
.query(async ({ input }) => {
|
|
return this.systemLogService.findUnique(input);
|
|
}),
|
|
|
|
// 通过ID获取日志详情(简化版)
|
|
findById: this.trpc.procedure
|
|
.input(z.string())
|
|
.query(async ({ input }) => {
|
|
return this.systemLogService.findUnique({
|
|
where: { id: input },
|
|
include: {
|
|
operator: {
|
|
select: {
|
|
id: true,
|
|
username: true,
|
|
showname: true,
|
|
}
|
|
},
|
|
department: {
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}),
|
|
|
|
// 记录人员操作日志的便捷方法
|
|
logStaffAction: this.trpc.protectProcedure
|
|
.input(z.object({
|
|
action: z.string(),
|
|
targetId: z.string(),
|
|
targetName: z.string(),
|
|
message: z.string().optional(),
|
|
beforeData: z.any().optional(),
|
|
afterData: z.any().optional(),
|
|
status: z.enum(['success', 'failure']).default('success'),
|
|
errorMessage: z.string().optional(),
|
|
}))
|
|
.mutation(async ({ input, ctx }) => {
|
|
const ipAddress = ctx.ip;
|
|
const operatorId = ctx.staff?.id;
|
|
|
|
try {
|
|
return this.systemLogService.create({
|
|
data: {
|
|
level: input.status === 'success' ? 'info' : 'error',
|
|
module: 'staff',
|
|
action: input.action,
|
|
operatorId: operatorId,
|
|
ipAddress: ipAddress,
|
|
targetId: input.targetId,
|
|
targetType: 'staff',
|
|
targetName: input.targetName,
|
|
message: input.message,
|
|
beforeData: input.beforeData,
|
|
afterData: input.afterData,
|
|
status: input.status,
|
|
errorMessage: input.errorMessage,
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('Error logging staff action:', error);
|
|
throw new Error('Failed to log staff action');
|
|
}
|
|
}),
|
|
|
|
// 高级搜索日志
|
|
searchLogs: this.trpc.procedure
|
|
.input(z.object({
|
|
page: z.number().default(1),
|
|
pageSize: z.number().default(20),
|
|
level: z.enum(['info', 'warning', 'error', 'debug']).optional(),
|
|
module: z.string().optional(),
|
|
action: z.string().optional(),
|
|
operatorId: z.string().optional(),
|
|
targetId: z.string().optional(),
|
|
targetType: z.string().optional(),
|
|
status: z.enum(['success', 'failure']).optional(),
|
|
startTime: z.string().optional(),
|
|
endTime: z.string().optional(),
|
|
keyword: z.string().optional(),
|
|
departmentId: z.string().optional(),
|
|
}))
|
|
.query(async ({ input }) => {
|
|
console.log('Received input for searchLogs:', input);
|
|
const where: Prisma.SystemLogWhereInput = {};
|
|
|
|
if (input.level) where.level = input.level;
|
|
if (input.module) where.module = input.module;
|
|
if (input.action) where.action = input.action;
|
|
if (input.operatorId) where.operatorId = input.operatorId;
|
|
if (input.targetId) where.targetId = input.targetId;
|
|
if (input.targetType) where.targetType = input.targetType;
|
|
if (input.status) where.status = input.status;
|
|
if (input.departmentId) where.departmentId = input.departmentId;
|
|
|
|
// 时间范围查询
|
|
if (input.startTime || input.endTime) {
|
|
where.timestamp = {};
|
|
if (input.startTime) where.timestamp.gte = new Date(input.startTime);
|
|
if (input.endTime) where.timestamp.lte = new Date(input.endTime);
|
|
}
|
|
// 关键词搜索
|
|
if (input.keyword) {
|
|
where.OR = [
|
|
{ targetName: { contains: input.keyword } },
|
|
{ action: { contains: input.keyword } },
|
|
{ module: { contains: input.keyword } },
|
|
{ errorMessage: { contains: input.keyword } },
|
|
];
|
|
}
|
|
|
|
try {
|
|
const result = await this.systemLogService.findManyWithPagination({
|
|
page: input.page,
|
|
pageSize: input.pageSize,
|
|
where,
|
|
select: {
|
|
id: true,
|
|
level: true,
|
|
module: true,
|
|
action: true,
|
|
timestamp: true,
|
|
operatorId: true,
|
|
ipAddress: true,
|
|
targetId: true,
|
|
targetType: true,
|
|
targetName: true,
|
|
details: true,
|
|
beforeData: true,
|
|
afterData: true,
|
|
status: true,
|
|
errorMessage: true,
|
|
departmentId: true,
|
|
operator: {
|
|
select: {
|
|
id: true,
|
|
username: true,
|
|
showname: true,
|
|
}
|
|
},
|
|
department: {
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
}
|
|
}
|
|
}
|
|
});
|
|
console.log('Search logs result:', result);
|
|
return result;
|
|
} catch (error) {
|
|
console.error('Error in searchLogs:', error);
|
|
throw new Error('Failed to search logs');
|
|
}
|
|
}),
|
|
})
|
|
} |