fix: simplify balance billing and govern operation logs
This commit is contained in:
@@ -180,8 +180,8 @@ export class OperationsService {
|
||||
_count: { _all: true },
|
||||
}),
|
||||
this.prisma.accountTransaction.aggregate({
|
||||
where: { tenantId: query.tenantId },
|
||||
_sum: { amountCents: true, smsUnits: true },
|
||||
where: { tenantId: query.tenantId, transactionType: 'refunded', createdAt: { gte: sinceToday } },
|
||||
_sum: { amountCents: true },
|
||||
_count: { _all: true },
|
||||
}),
|
||||
this.prisma.cmppConnectionState.groupBy({
|
||||
@@ -296,21 +296,31 @@ export class OperationsService {
|
||||
});
|
||||
}
|
||||
|
||||
auditLogs(query: { tenantId?: string; userId?: string }) {
|
||||
return this.prisma.operationLog.findMany({
|
||||
where: { tenantId: query.tenantId, userId: query.userId },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
async auditLogs(query: { tenantId?: string; userId?: string; page?: number; pageSize?: number }) {
|
||||
const page = positiveInteger(query.page, 1);
|
||||
const pageSize = Math.min(100, positiveInteger(query.pageSize, 20));
|
||||
const where: Prisma.OperationLogWhereInput = { tenantId: query.tenantId, userId: query.userId };
|
||||
const [items, total] = await Promise.all([
|
||||
this.prisma.operationLog.findMany({
|
||||
where,
|
||||
orderBy: [{ createdAt: 'desc' }, { id: 'desc' }],
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
}),
|
||||
this.prisma.operationLog.count({ where }),
|
||||
]);
|
||||
return { items, total, page, pageSize };
|
||||
}
|
||||
|
||||
async systemLogs(query: OperationLogQuery) {
|
||||
const page = Math.max(1, Number(query.page ?? 1));
|
||||
const pageSize = Math.min(100, Math.max(1, Number(query.pageSize ?? 10)));
|
||||
const page = positiveInteger(query.page, 1);
|
||||
const pageSize = Math.min(100, positiveInteger(query.pageSize, 10));
|
||||
const where: Prisma.OperationLogWhereInput = {
|
||||
tenantId: query.tenantId,
|
||||
userId: query.userId,
|
||||
createdAt: createdAtRange(query.range),
|
||||
resource: query.module && query.module !== 'all' ? query.module : undefined,
|
||||
AND: query.level && query.level !== 'all' ? operationLogLevelWhere(query.level) : undefined,
|
||||
OR: query.keyword ? [
|
||||
{ action: { contains: query.keyword } },
|
||||
{ resource: { contains: query.keyword } },
|
||||
@@ -324,7 +334,7 @@ export class OperationsService {
|
||||
this.prisma.operationLog.findMany({
|
||||
where,
|
||||
include: { tenant: true, user: true },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
orderBy: [{ createdAt: 'desc' }, { id: 'desc' }],
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
}),
|
||||
@@ -336,12 +346,9 @@ export class OperationsService {
|
||||
orderBy: { resource: 'asc' },
|
||||
}),
|
||||
]);
|
||||
const normalizedItems = items
|
||||
.map((item) => normalizeOperationLog(item))
|
||||
.filter((item) => !query.level || query.level === 'all' || item.level === query.level);
|
||||
return {
|
||||
items: normalizedItems,
|
||||
total: query.level && query.level !== 'all' ? normalizedItems.length : total,
|
||||
items: items.map((item) => normalizeOperationLog(item)),
|
||||
total,
|
||||
page,
|
||||
pageSize,
|
||||
modules: modules.map((item) => item.resource),
|
||||
@@ -722,7 +729,7 @@ export class OperationsService {
|
||||
relatedId: query.taskId,
|
||||
},
|
||||
_count: { _all: true },
|
||||
_sum: { amountCents: true, smsUnits: true },
|
||||
_sum: { amountCents: true },
|
||||
}),
|
||||
]);
|
||||
const messageAmount = messages._sum.amountCents ?? 0;
|
||||
@@ -969,6 +976,48 @@ function groupDownstreamByApplication(
|
||||
return [...summaryMap.values()];
|
||||
}
|
||||
|
||||
function positiveInteger(value: number | undefined, fallback: number) {
|
||||
const normalized = Number(value);
|
||||
return Number.isInteger(normalized) && normalized > 0 ? normalized : fallback;
|
||||
}
|
||||
|
||||
function operationLogLevelWhere(level: string): Prisma.OperationLogWhereInput {
|
||||
const error: Prisma.OperationLogWhereInput = {
|
||||
OR: [
|
||||
{ action: { contains: 'failed' } },
|
||||
{ action: { contains: 'reject' } },
|
||||
{ detail: { path: ['result'], string_contains: 'fail' } },
|
||||
{ detail: { path: ['status'], string_contains: 'fail' } },
|
||||
],
|
||||
};
|
||||
const warning: Prisma.OperationLogWhereInput = {
|
||||
OR: [
|
||||
{ action: { contains: 'warning' } },
|
||||
{ action: { contains: 'risk' } },
|
||||
],
|
||||
};
|
||||
const success: Prisma.OperationLogWhereInput = {
|
||||
OR: [
|
||||
{ action: { contains: 'approve' } },
|
||||
{ action: { contains: 'recharge' } },
|
||||
{ action: { contains: 'connected' } },
|
||||
],
|
||||
};
|
||||
if (level === 'error') {
|
||||
return error;
|
||||
}
|
||||
if (level === 'warning') {
|
||||
return { AND: [{ NOT: error }, warning] };
|
||||
}
|
||||
if (level === 'success') {
|
||||
return { AND: [{ NOT: error }, { NOT: warning }, success] };
|
||||
}
|
||||
if (level === 'info') {
|
||||
return { NOT: { OR: [error, warning, success] } };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
function normalizeOperationLog(log: Prisma.OperationLogGetPayload<{ include: { tenant: true; user: true } }>) {
|
||||
const detail = (log.detail ?? {}) as Record<string, unknown>;
|
||||
const result = String(detail.result ?? detail.status ?? '');
|
||||
|
||||
Reference in New Issue
Block a user