fix: align reporting fields queries and disk monitoring
This commit is contained in:
@@ -167,8 +167,9 @@ export class SmsApplicationConfigService {
|
||||
const merged = new Map<string, MergedReportField>();
|
||||
const routeChannels = new Map<string, { id: string; code: string; name: string; groupId: string; groupName: string }>();
|
||||
for (const route of routes) {
|
||||
if (!route.group) continue;
|
||||
if (!route.group || route.group.status === 'deleted') continue;
|
||||
for (const item of route.group.items) {
|
||||
if (item.channel.status === 'deleted') continue;
|
||||
if (!routeChannels.has(item.channel.id)) {
|
||||
routeChannels.set(item.channel.id, {
|
||||
id: item.channel.id,
|
||||
@@ -181,6 +182,17 @@ export class SmsApplicationConfigService {
|
||||
}
|
||||
}
|
||||
for (const configured of commonFields) {
|
||||
const existing = merged.get(configured.drainageField.id);
|
||||
if (existing) {
|
||||
existing.required ||= configured.required;
|
||||
if (!existing.reportTypes.includes(configured.reportType)) existing.reportTypes.push(configured.reportType);
|
||||
if (!existing.commonReportTypes.includes(configured.reportType)) existing.commonReportTypes.push(configured.reportType);
|
||||
for (const channel of existing.channels) {
|
||||
channel.required ||= configured.required;
|
||||
if (channel.reportType !== configured.reportType) channel.reportType = 'both';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
merged.set(configured.drainageField.id, {
|
||||
id: configured.drainageField.id,
|
||||
code: configured.drainageField.code,
|
||||
@@ -199,8 +211,9 @@ export class SmsApplicationConfigService {
|
||||
});
|
||||
}
|
||||
for (const route of routes) {
|
||||
if (!route.group) continue;
|
||||
if (!route.group || route.group.status === 'deleted') continue;
|
||||
for (const item of route.group.items) {
|
||||
if (item.channel.status === 'deleted') continue;
|
||||
for (const configured of item.channel.reportFields) {
|
||||
if (configured.status !== 'active' || !configured.drainageField || configured.drainageField.status !== 'active') continue;
|
||||
if (reportType && configured.reportType !== 'both' && configured.reportType !== reportType) continue;
|
||||
|
||||
@@ -181,7 +181,7 @@ export class SmsSignatureService {
|
||||
id: signatureId,
|
||||
tenantId,
|
||||
applicationId: query.applicationId,
|
||||
auditStatus: query.status || { notIn: ['deleted', 'disabled'] },
|
||||
auditStatus: { notIn: ['deleted', 'disabled'], ...(query.status && query.status !== 'all' ? { equals: query.status } : {}) },
|
||||
OR: query.keyword?.trim() ? [
|
||||
{ name: { contains: query.keyword.trim() } },
|
||||
{ purpose: { contains: query.keyword.trim() } },
|
||||
@@ -305,7 +305,7 @@ export class SmsSignatureService {
|
||||
const filteredWhere: Prisma.SmsSignatureWhereInput = {
|
||||
tenantId,
|
||||
applicationId: query.applicationId,
|
||||
auditStatus: query.status || { notIn: ['deleted', 'disabled'] },
|
||||
auditStatus: { notIn: ['deleted', 'disabled'], ...(query.status && query.status !== 'all' ? { equals: query.status } : {}) },
|
||||
OR: query.keyword?.trim() ? [
|
||||
{ name: { contains: query.keyword.trim() } },
|
||||
{ purpose: { contains: query.keyword.trim() } },
|
||||
@@ -427,7 +427,7 @@ export class SmsSignatureService {
|
||||
|
||||
async submitSignature(signatureId: string, tenantId?: string) {
|
||||
const signature = await this.prisma.smsSignature.findUnique({ where: { id: signatureId } });
|
||||
if (!signature || (tenantId && signature.tenantId !== tenantId)) {
|
||||
if (!signature || signature.auditStatus === 'deleted' || (tenantId && signature.tenantId !== tenantId)) {
|
||||
throw new NotFoundException('Signature not found');
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ function createPrismaMock() {
|
||||
create: jest.fn().mockResolvedValue({ id: 'drainage-record-1' }),
|
||||
},
|
||||
smsTemplate: {
|
||||
count: jest.fn().mockResolvedValue(0),
|
||||
findMany: jest.fn().mockResolvedValue([{
|
||||
id: 'tpl-1',
|
||||
tenantId: 'tenant-1',
|
||||
@@ -1024,6 +1025,56 @@ describe('SmsConfigService', () => {
|
||||
expect(prisma.channelSignatureReportTask.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('never lets client status filters bypass deleted signature exclusion and keeps template history filtered', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
prisma.smsSignature.findMany.mockResolvedValue([]);
|
||||
prisma.smsSignature.groupBy.mockResolvedValue([] as never);
|
||||
prisma.smsTemplate.findMany.mockResolvedValue([]);
|
||||
const service = new SmsConfigService(prisma as never);
|
||||
for (const status of ['deleted', 'all', 'approved']) {
|
||||
await service.getClientSignatureWorkspace('tenant-1', { status });
|
||||
const filter = { notIn: ['deleted', 'disabled'], ...(status !== 'all' ? { equals: status } : {}) };
|
||||
expect(prisma.smsSignature.findMany).toHaveBeenLastCalledWith(expect.objectContaining({ where: expect.objectContaining({ tenantId: 'tenant-1', auditStatus: filter }) }));
|
||||
expect(prisma.smsSignature.count).toHaveBeenLastCalledWith(expect.objectContaining({ where: expect.objectContaining({ auditStatus: filter }) }));
|
||||
}
|
||||
await service.listClientTemplates('tenant-1', true);
|
||||
expect(prisma.smsTemplate.findMany).toHaveBeenLastCalledWith(expect.objectContaining({ where: expect.objectContaining({ tenantId: 'tenant-1', auditStatus: { not: 'deleted' } }) }));
|
||||
await service.listTemplatesPage({ tenantId: 'tenant-1', status: 'all', page: 1, pageSize: 10 });
|
||||
expect(prisma.smsTemplate.count).toHaveBeenLastCalledWith(expect.objectContaining({ where: expect.objectContaining({ auditStatus: { not: 'deleted' } }) }));
|
||||
prisma.smsSignature.findUnique.mockResolvedValue({ id: 'sig-deleted', tenantId: 'tenant-1', auditStatus: 'deleted' } as never);
|
||||
prisma.smsTemplate.findUnique.mockResolvedValue({ id: 'tpl-deleted', tenantId: 'tenant-1', auditStatus: 'deleted' } as never);
|
||||
await expect(service.submitSignature('sig-deleted', 'tenant-1')).rejects.toThrow('not found');
|
||||
await expect(service.submitTemplate('tpl-deleted', 'tenant-1')).rejects.toThrow('not found');
|
||||
});
|
||||
|
||||
it('uses exactly the same editable common fields in client and admin signature forms', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
const field = { id: 'field-common', code: 'license', name: '主体证明', fieldType: 'file', description: '最新配置', status: 'active' };
|
||||
prisma.commonReportField.findMany.mockResolvedValue([{ id: 'common-1', reportType: 'signature', required: true, drainageField: field }]);
|
||||
const service = new SmsConfigService(prisma as never);
|
||||
for (const applicationId of [undefined, 'app-1']) {
|
||||
const admin = await service.getApplicationReportFields(applicationId, 'signature');
|
||||
const client = await service.getClientApplicationReportFields(applicationId, 'signature');
|
||||
expect(client).toEqual(admin.map((item) => Object.fromEntries(Object.entries(item).filter(([key]) => !['channels', 'commonReportTypes'].includes(key)))));
|
||||
expect(client[0]).toMatchObject({ code: 'license', name: '主体证明', fieldType: 'file', required: true, description: '最新配置' });
|
||||
}
|
||||
prisma.commonReportField.findMany.mockResolvedValue([{ id: 'common-1', reportType: 'signature', required: false, drainageField: field }]);
|
||||
expect((await service.getClientApplicationReportFields(undefined, 'signature'))[0].required).toBe(false);
|
||||
});
|
||||
|
||||
it('keeps both common field purposes in material snapshots and ignores deleted channel requirements', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
const field = { id: 'field-1', code: 'license', name: '主体证明', fieldType: 'file', status: 'active' };
|
||||
prisma.commonReportField.findMany.mockResolvedValue([
|
||||
{ reportType: 'signature', required: true, drainageField: field },
|
||||
{ reportType: 'drainage', required: false, drainageField: field },
|
||||
]);
|
||||
prisma.channelRouteRule.findMany.mockResolvedValue([{ group: { id: 'group-1', items: [{ channel: { id: 'deleted-channel', status: 'deleted', reportFields: [{ status: 'active', reportType: 'signature', required: true, drainageField: { ...field, id: 'old', code: 'old' } }] } }] } }] as never);
|
||||
const result = await new SmsConfigService(prisma as never).getApplicationReportFields('app-1');
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchObject({ required: true, reportTypes: ['signature', 'drainage'], commonReportTypes: ['signature', 'drainage'], channels: [] });
|
||||
});
|
||||
|
||||
it('accepts a scheme-less drainage URL and synchronizes the compatibility name', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
prisma.smsSignature.findUnique.mockResolvedValue({ id: 'sig-1', tenantId: 'tenant-1', applicationId: 'app-1', auditStatus: 'approved' });
|
||||
|
||||
@@ -176,7 +176,7 @@ export class SmsTemplateService {
|
||||
|
||||
async submitTemplate(templateId: string, tenantId?: string) {
|
||||
const template = await this.prisma.smsTemplate.findUnique({ where: { id: templateId } });
|
||||
if (!template || (tenantId && template.tenantId !== tenantId)) {
|
||||
if (!template || template.auditStatus === 'deleted' || (tenantId && template.tenantId !== tenantId)) {
|
||||
throw new NotFoundException('Template not found');
|
||||
}
|
||||
await this.validateTemplateSignature(template.signatureId, template.tenantId, template.applicationId, template.content);
|
||||
|
||||
Reference in New Issue
Block a user