fix: paginate operational list pages
This commit is contained in:
@@ -12,8 +12,14 @@ export class AdminSmsConfigController {
|
||||
constructor(private readonly smsConfig: SmsConfigService, private readonly reviewGovernance: ReviewGovernanceService, private readonly deletions: DeletionGovernanceService) {}
|
||||
|
||||
@Get('enterprise-applications')
|
||||
listApplications(@Query('tenantId') tenantId?: string, @Query('keyword') keyword?: string, @Query('enterpriseKeyword') enterpriseKeyword?: string, @Query('applicationKeyword') applicationKeyword?: string, @Query('status') status?: string) {
|
||||
return this.smsConfig.listApplications({ tenantId, keyword, enterpriseKeyword, applicationKeyword, status, includeConnections: true });
|
||||
listApplications(@Query('tenantId') tenantId?: string, @Query('keyword') keyword?: string, @Query('enterpriseKeyword') enterpriseKeyword?: string, @Query('applicationKeyword') applicationKeyword?: string, @Query('status') status?: string, @Query('page') page?: string, @Query('pageSize') pageSize?: string) {
|
||||
const query = { tenantId, keyword, enterpriseKeyword, applicationKeyword, status, includeConnections: true, page: Number(page), pageSize: Number(pageSize) };
|
||||
return page || pageSize ? this.smsConfig.listApplicationsPage(query) : this.smsConfig.listApplications(query);
|
||||
}
|
||||
|
||||
@Get('enterprise-application-options')
|
||||
listApplicationOptions(@Query('tenantId') tenantId?: string) {
|
||||
return this.smsConfig.listApplicationOptions(tenantId);
|
||||
}
|
||||
|
||||
@Get('enterprise-applications/:id')
|
||||
@@ -65,8 +71,14 @@ export class AdminSmsConfigController {
|
||||
}
|
||||
|
||||
@Get('enterprise-signatures')
|
||||
listSignatures(@Query('tenantId') tenantId?: string, @Query('keyword') keyword?: string, @Query('status') status?: string, @Query('enterpriseKeyword') enterpriseKeyword?: string, @Query('applicationKeyword') applicationKeyword?: string, @Query('signatureKeyword') signatureKeyword?: string, @Query('drainageKeyword') drainageKeyword?: string) {
|
||||
return this.smsConfig.listSignatures({ tenantId, keyword, status, enterpriseKeyword, applicationKeyword, signatureKeyword, drainageKeyword });
|
||||
listSignatures(@Query('tenantId') tenantId?: string, @Query('keyword') keyword?: string, @Query('status') status?: string, @Query('enterpriseKeyword') enterpriseKeyword?: string, @Query('applicationKeyword') applicationKeyword?: string, @Query('signatureKeyword') signatureKeyword?: string, @Query('drainageKeyword') drainageKeyword?: string, @Query('page') page?: string, @Query('pageSize') pageSize?: string) {
|
||||
const query = { tenantId, keyword, status, enterpriseKeyword, applicationKeyword, signatureKeyword, drainageKeyword, page: Number(page), pageSize: Number(pageSize) };
|
||||
return page || pageSize ? this.smsConfig.listSignaturesPage(query) : this.smsConfig.listSignatures(query);
|
||||
}
|
||||
|
||||
@Get('enterprise-signature-options')
|
||||
listSignatureOptions(@Query('tenantId') tenantId?: string) {
|
||||
return this.smsConfig.listSignatureOptions(tenantId);
|
||||
}
|
||||
|
||||
@Post('enterprise-signatures')
|
||||
@@ -113,8 +125,9 @@ export class AdminSmsConfigController {
|
||||
}
|
||||
|
||||
@Get('enterprise-templates')
|
||||
listTemplates(@Query('tenantId') tenantId?: string, @Query('status') status?: string, @Query('keyword') keyword?: string, @Query('enterpriseKeyword') enterpriseKeyword?: string, @Query('applicationKeyword') applicationKeyword?: string, @Query('nameKeyword') nameKeyword?: string, @Query('contentKeyword') contentKeyword?: string) {
|
||||
return this.smsConfig.listTemplates({ tenantId, status, keyword, enterpriseKeyword, applicationKeyword, nameKeyword, contentKeyword });
|
||||
listTemplates(@Query('tenantId') tenantId?: string, @Query('status') status?: string, @Query('keyword') keyword?: string, @Query('enterpriseKeyword') enterpriseKeyword?: string, @Query('applicationKeyword') applicationKeyword?: string, @Query('nameKeyword') nameKeyword?: string, @Query('contentKeyword') contentKeyword?: string, @Query('page') page?: string, @Query('pageSize') pageSize?: string) {
|
||||
const query = { tenantId, status, keyword, enterpriseKeyword, applicationKeyword, nameKeyword, contentKeyword, page: Number(page), pageSize: Number(pageSize) };
|
||||
return page || pageSize ? this.smsConfig.listTemplatesPage(query) : this.smsConfig.listTemplates(query);
|
||||
}
|
||||
|
||||
@Post('enterprise-templates')
|
||||
|
||||
@@ -23,8 +23,15 @@ export class ClientSmsConfigController {
|
||||
constructor(private readonly smsConfig: SmsConfigService, private readonly deletions: DeletionGovernanceService) {}
|
||||
|
||||
@Get('applications')
|
||||
listApplications(@TenantId() tenantId?: string) {
|
||||
return this.smsConfig.listApplications(tenantId);
|
||||
listApplications(@TenantId() tenantId?: string, @Query('page') page?: string, @Query('pageSize') pageSize?: string) {
|
||||
return page || pageSize
|
||||
? this.smsConfig.listApplicationsPage({ tenantId, page: Number(page), pageSize: Number(pageSize) })
|
||||
: this.smsConfig.listApplications(tenantId);
|
||||
}
|
||||
|
||||
@Get('application-options')
|
||||
listApplicationOptions(@TenantId() tenantId?: string) {
|
||||
return this.smsConfig.listApplicationOptions(tenantId);
|
||||
}
|
||||
|
||||
@Post('applications')
|
||||
@@ -64,9 +71,14 @@ export class ClientSmsConfigController {
|
||||
return this.smsConfig.listClientSignatures(tenantId);
|
||||
}
|
||||
|
||||
@Get('signature-options')
|
||||
listSignatureOptions(@TenantId() tenantId?: string) {
|
||||
return this.smsConfig.listSignatureOptions(tenantId);
|
||||
}
|
||||
|
||||
@Get('signatures-workspace')
|
||||
getSignatureWorkspace(@TenantId() tenantId?: string) {
|
||||
return this.smsConfig.getClientSignatureWorkspace(tenantId);
|
||||
getSignatureWorkspace(@TenantId() tenantId?: string, @Query('keyword') keyword?: string, @Query('applicationId') applicationId?: string, @Query('status') status?: string, @Query('page') page?: string, @Query('pageSize') pageSize?: string) {
|
||||
return this.smsConfig.getClientSignatureWorkspace(tenantId, { keyword, applicationId, status, page: Number(page), pageSize: Number(pageSize) });
|
||||
}
|
||||
|
||||
@Post('signatures')
|
||||
@@ -124,8 +136,10 @@ export class ClientSmsConfigController {
|
||||
}
|
||||
|
||||
@Get('templates')
|
||||
listTemplates(@TenantId() tenantId?: string, @Query('includeHistory') includeHistory?: string) {
|
||||
return this.smsConfig.listClientTemplates(tenantId, includeHistory === 'true');
|
||||
listTemplates(@TenantId() tenantId?: string, @Query('includeHistory') includeHistory?: string, @Query('keyword') keyword?: string, @Query('page') page?: string, @Query('pageSize') pageSize?: string) {
|
||||
return page || pageSize
|
||||
? this.smsConfig.listTemplatesPage({ tenantId, status: includeHistory === 'true' ? 'all' : 'approved', keyword, page: Number(page), pageSize: Number(pageSize) })
|
||||
: this.smsConfig.listClientTemplates(tenantId, includeHistory === 'true');
|
||||
}
|
||||
|
||||
@Post('templates')
|
||||
|
||||
@@ -69,6 +69,7 @@ function createPrismaMock() {
|
||||
},
|
||||
smsSignature: {
|
||||
groupBy: jest.fn().mockResolvedValue([]),
|
||||
count: jest.fn().mockResolvedValue(0),
|
||||
findMany: jest.fn().mockResolvedValue([{
|
||||
id: 'sig-1',
|
||||
tenantId: 'tenant-1',
|
||||
@@ -900,6 +901,7 @@ describe('SmsConfigService', () => {
|
||||
it('returns real client signature workspace counts from database grouping', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
prisma.smsSignature.findMany.mockResolvedValue([]);
|
||||
prisma.smsSignature.count.mockResolvedValue(8);
|
||||
prisma.smsSignature.groupBy.mockResolvedValue([
|
||||
{ auditStatus: 'pending', _count: { _all: 2 } },
|
||||
{ auditStatus: 'approved', _count: { _all: 5 } },
|
||||
@@ -910,6 +912,9 @@ describe('SmsConfigService', () => {
|
||||
await expect(service.getClientSignatureWorkspace('tenant-1')).resolves.toEqual({
|
||||
items: [],
|
||||
summary: { total: 8, pending: 2, approved: 5, rejected: 1, draft: 0 },
|
||||
total: 8,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
expect(prisma.smsSignature.groupBy).toHaveBeenCalledWith(expect.objectContaining({
|
||||
where: { tenantId: 'tenant-1', auditStatus: { notIn: ['deleted', 'disabled'] } },
|
||||
|
||||
@@ -122,6 +122,8 @@ export interface TemplateListQuery {
|
||||
applicationKeyword?: string;
|
||||
nameKeyword?: string;
|
||||
contentKeyword?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
export interface ApplicationListQuery {
|
||||
@@ -131,6 +133,8 @@ export interface ApplicationListQuery {
|
||||
applicationKeyword?: string;
|
||||
status?: string;
|
||||
includeConnections?: boolean;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
export interface SignatureListQuery {
|
||||
@@ -141,6 +145,8 @@ export interface SignatureListQuery {
|
||||
applicationKeyword?: string;
|
||||
signatureKeyword?: string;
|
||||
drainageKeyword?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
export interface GatewayDownstreamConnectionEventDto {
|
||||
@@ -205,7 +211,15 @@ export class SmsConfigService implements OnModuleInit, OnModuleDestroy {
|
||||
ipAllowlist: true,
|
||||
httpConfig: true,
|
||||
},
|
||||
omit: {
|
||||
secretHash: true,
|
||||
passwordCipher: true,
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
...(query.page && query.pageSize ? {
|
||||
skip: (query.page - 1) * query.pageSize,
|
||||
take: query.pageSize,
|
||||
} : {}),
|
||||
});
|
||||
if (!query.includeConnections) {
|
||||
return applications;
|
||||
@@ -243,6 +257,34 @@ export class SmsConfigService implements OnModuleInit, OnModuleDestroy {
|
||||
|| left.id.localeCompare(right.id));
|
||||
}
|
||||
|
||||
async listApplicationsPage(query: ApplicationListQuery) {
|
||||
const page = Math.max(1, Math.floor(Number(query.page) || 1));
|
||||
const pageSize = Math.min(100, Math.max(1, Math.floor(Number(query.pageSize) || 10)));
|
||||
const where: Prisma.SmsApplicationWhereInput = {
|
||||
tenantId: query.tenantId,
|
||||
status: query.status && query.status !== 'all' ? query.status : { not: 'deleted' },
|
||||
tenant: query.enterpriseKeyword ? { name: { contains: query.enterpriseKeyword } } : undefined,
|
||||
name: query.applicationKeyword ? { contains: query.applicationKeyword } : undefined,
|
||||
OR: query.keyword ? [
|
||||
{ name: { contains: query.keyword } },
|
||||
{ tenant: { name: { contains: query.keyword } } },
|
||||
] : undefined,
|
||||
};
|
||||
const [items, total] = await Promise.all([
|
||||
this.listApplications({ ...query, page, pageSize }),
|
||||
this.prisma.smsApplication.count({ where }),
|
||||
]);
|
||||
return { items, total, page, pageSize };
|
||||
}
|
||||
|
||||
listApplicationOptions(tenantId?: string) {
|
||||
return this.prisma.smsApplication.findMany({
|
||||
where: { tenantId, status: { not: 'deleted' } },
|
||||
select: { id: true, tenantId: true, name: true, status: true },
|
||||
orderBy: [{ name: 'asc' }, { id: 'asc' }],
|
||||
});
|
||||
}
|
||||
|
||||
async getApplication(applicationId: string, tenantId?: string) {
|
||||
const application = await this.prisma.smsApplication.findUnique({
|
||||
where: { id: applicationId },
|
||||
@@ -905,6 +947,10 @@ export class SmsConfigService implements OnModuleInit, OnModuleDestroy {
|
||||
reportTasks: { include: { channel: true, drainageInfo: true } },
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
...(query.page && query.pageSize ? {
|
||||
skip: (query.page - 1) * query.pageSize,
|
||||
take: query.pageSize,
|
||||
} : {}),
|
||||
});
|
||||
const applicationIds = signatures.map((signature) => signature.applicationId).filter((id): id is string => Boolean(id));
|
||||
const routes = applicationIds.length ? await this.prisma.channelRouteRule.findMany({
|
||||
@@ -979,9 +1025,60 @@ export class SmsConfigService implements OnModuleInit, OnModuleDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
async listClientSignatures(tenantId?: string, signatureId?: string) {
|
||||
async listSignaturesPage(query: SignatureListQuery) {
|
||||
const page = Math.max(1, Math.floor(Number(query.page) || 1));
|
||||
const pageSize = Math.min(100, Math.max(1, Math.floor(Number(query.pageSize) || 10)));
|
||||
const where: Prisma.SmsSignatureWhereInput = {
|
||||
tenantId: query.tenantId,
|
||||
auditStatus: query.status && query.status !== 'all' ? query.status : { not: 'deleted' },
|
||||
tenant: query.enterpriseKeyword ? { name: { contains: query.enterpriseKeyword } } : undefined,
|
||||
application: query.applicationKeyword ? { name: { contains: query.applicationKeyword } } : undefined,
|
||||
name: query.signatureKeyword ? { contains: query.signatureKeyword } : undefined,
|
||||
drainageItems: query.drainageKeyword ? {
|
||||
some: {
|
||||
auditStatus: { not: 'deleted' },
|
||||
OR: [
|
||||
{ siteName: { contains: query.drainageKeyword } },
|
||||
{ url: { contains: query.drainageKeyword } },
|
||||
{ remark: { contains: query.drainageKeyword } },
|
||||
],
|
||||
},
|
||||
} : undefined,
|
||||
OR: query.keyword ? [
|
||||
{ name: { contains: query.keyword } },
|
||||
{ purpose: { contains: query.keyword } },
|
||||
{ tenant: { name: { contains: query.keyword } } },
|
||||
{ application: { name: { contains: query.keyword } } },
|
||||
] : undefined,
|
||||
};
|
||||
const [items, total] = await Promise.all([
|
||||
this.listSignatures({ ...query, page, pageSize }),
|
||||
this.prisma.smsSignature.count({ where }),
|
||||
]);
|
||||
return { items, total, page, pageSize };
|
||||
}
|
||||
|
||||
listSignatureOptions(tenantId?: string) {
|
||||
return this.prisma.smsSignature.findMany({
|
||||
where: { tenantId, auditStatus: { not: 'deleted' } },
|
||||
select: { id: true, tenantId: true, applicationId: true, name: true, auditStatus: true },
|
||||
orderBy: [{ name: 'asc' }, { id: 'asc' }],
|
||||
});
|
||||
}
|
||||
|
||||
async listClientSignatures(tenantId?: string, signatureId?: string, query: { keyword?: string; applicationId?: string; status?: string; page?: number; pageSize?: number } = {}) {
|
||||
const signatures = await this.prisma.smsSignature.findMany({
|
||||
where: { id: signatureId, tenantId, auditStatus: { notIn: ['deleted', 'disabled'] } },
|
||||
where: {
|
||||
id: signatureId,
|
||||
tenantId,
|
||||
applicationId: query.applicationId,
|
||||
auditStatus: query.status || { notIn: ['deleted', 'disabled'] },
|
||||
OR: query.keyword?.trim() ? [
|
||||
{ name: { contains: query.keyword.trim() } },
|
||||
{ purpose: { contains: query.keyword.trim() } },
|
||||
{ application: { name: { contains: query.keyword.trim() } } },
|
||||
] : undefined,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
tenantId: true,
|
||||
@@ -1020,6 +1117,8 @@ export class SmsConfigService implements OnModuleInit, OnModuleDestroy {
|
||||
_count: { select: { reportMaterials: true } },
|
||||
},
|
||||
orderBy: { updatedAt: 'desc' },
|
||||
skip: query.page && query.pageSize ? (query.page - 1) * query.pageSize : undefined,
|
||||
take: query.pageSize,
|
||||
});
|
||||
return signatures.map((signature) => {
|
||||
const stored = isRecord(signature.drainageInfo) ? signature.drainageInfo : {};
|
||||
@@ -1056,9 +1155,22 @@ export class SmsConfigService implements OnModuleInit, OnModuleDestroy {
|
||||
return signature;
|
||||
}
|
||||
|
||||
async getClientSignatureWorkspace(tenantId?: string) {
|
||||
const [items, statusCounts] = await Promise.all([
|
||||
this.listClientSignatures(tenantId),
|
||||
async getClientSignatureWorkspace(tenantId?: string, query: { keyword?: string; applicationId?: string; status?: string; page?: number; pageSize?: number } = {}) {
|
||||
const page = Math.max(1, Math.floor(Number(query.page) || 1));
|
||||
const pageSize = Math.min(100, Math.max(1, Math.floor(Number(query.pageSize) || 10)));
|
||||
const filteredWhere: Prisma.SmsSignatureWhereInput = {
|
||||
tenantId,
|
||||
applicationId: query.applicationId,
|
||||
auditStatus: query.status || { notIn: ['deleted', 'disabled'] },
|
||||
OR: query.keyword?.trim() ? [
|
||||
{ name: { contains: query.keyword.trim() } },
|
||||
{ purpose: { contains: query.keyword.trim() } },
|
||||
{ application: { name: { contains: query.keyword.trim() } } },
|
||||
] : undefined,
|
||||
};
|
||||
const [items, total, statusCounts] = await Promise.all([
|
||||
this.listClientSignatures(tenantId, undefined, { ...query, page, pageSize }),
|
||||
this.prisma.smsSignature.count({ where: filteredWhere }),
|
||||
this.prisma.smsSignature.groupBy({
|
||||
by: ['auditStatus'],
|
||||
where: { tenantId, auditStatus: { notIn: ['deleted', 'disabled'] } },
|
||||
@@ -1073,7 +1185,7 @@ export class SmsConfigService implements OnModuleInit, OnModuleDestroy {
|
||||
summary[item.auditStatus as keyof Omit<typeof summary, 'total'>] = count;
|
||||
}
|
||||
}
|
||||
return { items, summary };
|
||||
return { items, summary, total, page, pageSize };
|
||||
}
|
||||
|
||||
async listClientDrainageInfos(tenantId?: string, itemId?: string) {
|
||||
@@ -1464,9 +1576,38 @@ export class SmsConfigService implements OnModuleInit, OnModuleDestroy {
|
||||
},
|
||||
include: { variables: true, application: true, tenant: true, signature: true },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
...(query.page && query.pageSize ? {
|
||||
skip: (query.page - 1) * query.pageSize,
|
||||
take: query.pageSize,
|
||||
} : {}),
|
||||
});
|
||||
}
|
||||
|
||||
async listTemplatesPage(query: TemplateListQuery) {
|
||||
const page = Math.max(1, Math.floor(Number(query.page) || 1));
|
||||
const pageSize = Math.min(100, Math.max(1, Math.floor(Number(query.pageSize) || 10)));
|
||||
const where: Prisma.SmsTemplateWhereInput = {
|
||||
tenantId: query.tenantId,
|
||||
auditStatus: query.status && query.status !== 'all' ? query.status : { not: 'deleted' },
|
||||
tenant: query.enterpriseKeyword ? { name: { contains: query.enterpriseKeyword } } : undefined,
|
||||
application: query.applicationKeyword ? { name: { contains: query.applicationKeyword } } : undefined,
|
||||
name: query.nameKeyword ? { contains: query.nameKeyword } : undefined,
|
||||
content: query.contentKeyword ? { contains: query.contentKeyword } : undefined,
|
||||
OR: query.keyword ? [
|
||||
{ name: { contains: query.keyword } },
|
||||
{ content: { contains: query.keyword } },
|
||||
{ category: { contains: query.keyword } },
|
||||
{ application: { name: { contains: query.keyword } } },
|
||||
{ tenant: { name: { contains: query.keyword } } },
|
||||
] : undefined,
|
||||
};
|
||||
const [items, total] = await Promise.all([
|
||||
this.listTemplates({ ...query, page, pageSize }),
|
||||
this.prisma.smsTemplate.count({ where }),
|
||||
]);
|
||||
return { items, total, page, pageSize };
|
||||
}
|
||||
|
||||
listClientTemplates(tenantId: string | undefined, includeHistory = false) {
|
||||
return this.listTemplates({ tenantId, status: includeHistory ? 'all' : 'approved' });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user