feat: optimize signature workflows and high-frequency queries

This commit is contained in:
hectorzhao
2026-09-02 15:45:48 +08:00
parent 9b8196ecab
commit ad89e8fed7
48 changed files with 1334 additions and 352 deletions
@@ -82,6 +82,21 @@ export class AdminSmsConfigController {
return this.smsConfig.listSignatureOptions(tenantId);
}
@Get('enterprise-signatures/:id')
getSignature(@Param('id') id: string) {
return this.smsConfig.getSignature(id);
}
@Get('enterprise-signatures/:id/report-targets')
getSignatureReportTargets(@Param('id') id: string) {
return this.smsConfig.getSignatureReportTargets(id);
}
@Get('drainage-infos/:id/report-targets')
getDrainageReportTargets(@Param('id') id: string) {
return this.smsConfig.getDrainageReportTargets(id);
}
@Post('enterprise-signatures')
createSignature(@Body() body: CreateSmsSignatureDto) {
return this.smsConfig.createSignature(body, { initialAuditStatus: 'approved' });
+109 -10
View File
@@ -72,12 +72,13 @@ export class SmsSignatureService {
private readonly reportValidation: SmsReportValidationService,
private readonly audit: SmsAuditService,
) {}
async listSignatures(queryOrTenantId?: string | SignatureListQuery) {
async listSignatures(queryOrTenantId?: string | SignatureListQuery, summaryOnly = false) {
const query = typeof queryOrTenantId === 'string' ? { tenantId: queryOrTenantId } : (queryOrTenantId ?? {});
const signatureSort =
query.signatureSort === 'asc' || query.signatureSort === 'desc' ? query.signatureSort : undefined;
const signatures = await this.prisma.smsSignature.findMany({
where: {
id: query.signatureId,
tenantId: query.tenantId,
auditStatus: query.status && query.status !== 'all' ? query.status : { not: 'deleted' },
tenant: query.enterpriseKeyword ? { name: { contains: query.enterpriseKeyword } } : undefined,
@@ -105,12 +106,54 @@ export class SmsSignatureService {
]
: undefined,
},
include: {
materials: true,
tenant: true,
application: true,
drainageItems: { where: { auditStatus: { not: 'deleted' } }, orderBy: { updatedAt: 'desc' } },
reportTasks: { include: { channel: true, drainageInfo: true } },
select: {
id: true,
tenantId: true,
applicationId: true,
name: true,
purpose: true,
drainageInfo: true,
auditStatus: true,
reportStatus: true,
rejectReason: true,
materialVersion: true,
pendingReport: true,
reportChangedAt: true,
createdAt: true,
updatedAt: true,
materials: !summaryOnly,
tenant: { select: { id: true, name: true, code: true, status: true } },
application: { select: { id: true, tenantId: true, name: true, status: true } },
drainageItems: {
where: { auditStatus: { not: 'deleted' } },
orderBy: { updatedAt: 'desc' },
select: {
id: true,
siteName: true,
url: true,
remark: true,
reportValues: !summaryOnly,
auditStatus: true,
rejectReason: true,
submittedAt: true,
reviewedAt: true,
createdAt: true,
updatedAt: true,
},
},
reportTasks: {
select: {
id: true,
signatureId: true,
channelId: true,
carrier: true,
status: true,
approvedAt: true,
approvalScope: true,
reportType: true,
drainageItemId: true,
},
},
reportBatchItems: {
where: { batch: { status: { in: ['completed', 'partial_failed'] } } },
select: { reportType: true, materialVersion: true, snapshot: true },
@@ -130,7 +173,28 @@ export class SmsSignatureService {
const routes = applicationIds.length
? await this.prisma.channelRouteRule.findMany({
where: { applicationId: { in: applicationIds }, status: 'active' },
include: { group: { include: { items: { include: { channel: { include: { reportFields: true } } } } } } },
select: {
applicationId: true,
group: {
select: {
status: true,
items: {
select: {
channel: {
select: {
id: true,
name: true,
carrier: true,
carriers: true,
status: true,
reportFields: { select: { status: true, reportType: true } },
},
},
},
},
},
},
},
})
: [];
const hasCommonDrainageFields = await this.prisma.commonReportField
@@ -208,7 +272,7 @@ export class SmsSignatureService {
createdAt: item.createdAt.toISOString(),
updatedAt: item.updatedAt.toISOString(),
}));
return {
const view = {
...signatureView,
name: normalizeSmsSignature(signature.name),
drainageInfo: { ...legacyPayload, links: drainageLinks },
@@ -330,6 +394,20 @@ export class SmsSignatureService {
}),
),
};
if (!summaryOnly) return view;
const {
materials: _materials,
reportTasks: _reportTasks,
reportTargets: _reportTargets,
drainageReportTargets: _drainageReportTargets,
...summary
} = view;
return {
...summary,
drainageInfo: {
links: drainageLinks.map(({ reportValues: _reportValues, ...link }) => link),
},
};
});
}
@@ -365,12 +443,33 @@ export class SmsSignatureService {
: undefined,
};
const [items, total] = await Promise.all([
this.listSignatures({ ...query, page, pageSize }),
this.listSignatures({ ...query, page, pageSize }, true),
this.prisma.smsSignature.count({ where }),
]);
return { items, total, page, pageSize };
}
async getSignature(id: string) {
const [item] = await this.listSignatures({ signatureId: id });
if (!item || item.auditStatus === 'deleted') throw new NotFoundException('Signature not found');
return item;
}
async getSignatureReportTargets(id: string) {
const item = await this.getSignature(id);
return 'reportTargets' in item ? item.reportTargets ?? [] : [];
}
async getDrainageReportTargets(id: string) {
const drainage = await this.prisma.smsDrainageInfo.findUnique({
where: { id },
select: { id: true, signatureId: true, auditStatus: true },
});
if (!drainage || drainage.auditStatus === 'deleted') throw new NotFoundException('Drainage info not found');
const signature = await this.getSignature(drainage.signatureId);
return 'drainageReportTargets' in signature ? signature.drainageReportTargets?.[id] ?? [] : [];
}
listSignatureOptions(tenantId?: string) {
return this.prisma.smsSignature.findMany({
where: { tenantId, auditStatus: { not: 'deleted' } },
@@ -136,6 +136,7 @@ export interface ApplicationListQuery {
}
export interface SignatureListQuery {
signatureId?: string;
tenantId?: string;
keyword?: string;
status?: string;
+20 -5
View File
@@ -796,12 +796,12 @@ describe('SmsConfigService', () => {
name: { contains: '签名' },
drainageItems: expect.objectContaining({ some: expect.objectContaining({ OR: expect.any(Array) }) }),
}),
include: expect.objectContaining({
select: expect.objectContaining({
materials: true,
tenant: true,
application: true,
drainageItems: { where: { auditStatus: { not: 'deleted' } }, orderBy: { updatedAt: 'desc' } },
reportTasks: { include: { channel: true, drainageInfo: true } },
tenant: { select: { id: true, name: true, code: true, status: true } },
application: { select: { id: true, tenantId: true, name: true, status: true } },
drainageItems: expect.objectContaining({ where: { auditStatus: { not: 'deleted' } }, orderBy: { updatedAt: 'desc' } }),
reportTasks: expect.objectContaining({ select: expect.any(Object) }),
reportBatchItems: expect.any(Object),
}),
orderBy: [{ name: 'asc' }, { id: 'asc' }],
@@ -1215,6 +1215,21 @@ describe('SmsConfigService', () => {
expect(prisma.smsDrainageInfo.findMany).toHaveBeenCalledWith(expect.objectContaining({ where: expect.objectContaining({ submittedAt: expectedRange }) }));
});
it('returns a signature page summary without edit materials or report target arrays', async () => {
const prisma = createPrismaMock();
prisma.smsSignature.count.mockResolvedValue(1);
const service = new SmsConfigService(prisma as never);
const result = await service.listSignaturesPage({ page: 1, pageSize: 10 });
expect(result.total).toBe(1);
expect(result.items[0]).toEqual(expect.objectContaining({ id: 'sig-1', name: '【签名A】' }));
expect(result.items[0]).not.toHaveProperty('materials');
expect(result.items[0]).not.toHaveProperty('reportTasks');
expect(result.items[0]).not.toHaveProperty('reportTargets');
expect(result.items[0]).not.toHaveProperty('drainageReportTargets');
});
it.each([
'【带 空格】',
' 【外部空格】',
+12
View File
@@ -123,6 +123,18 @@ export class SmsConfigService implements OnModuleInit, OnModuleDestroy {
return this.signatures.listSignaturesPage(query);
}
async getSignature(id: string) {
return this.signatures.getSignature(id);
}
async getSignatureReportTargets(id: string) {
return this.signatures.getSignatureReportTargets(id);
}
async getDrainageReportTargets(id: string) {
return this.signatures.getDrainageReportTargets(id);
}
listSignatureOptions(tenantId?: string) {
return this.signatures.listSignatureOptions(tenantId);
}