feat: inherit channel report requirements

This commit is contained in:
hectorzhao
2026-07-12 16:14:54 +08:00
parent 053bcca990
commit 87ae4a2063
14 changed files with 612 additions and 65 deletions
@@ -68,6 +68,13 @@ function createPrismaMock() {
findUnique: jest.fn().mockResolvedValue({ id: 'sig-1', tenantId: 'tenant-1', auditStatus: 'pending' }),
update: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'sig-1', tenantId: 'tenant-1', ...data })),
},
signatureReportMaterial: {
upsert: jest.fn().mockResolvedValue({ id: 'signature-report-value-1' }),
},
drainageReportMaterial: {
upsert: jest.fn().mockResolvedValue({ id: 'drainage-report-value-1' }),
deleteMany: jest.fn().mockResolvedValue({ count: 0 }),
},
smsTemplate: {
findMany: jest.fn().mockResolvedValue([{
id: 'tpl-1',
@@ -427,6 +434,76 @@ describe('SmsConfigService', () => {
}));
});
it('merges report fields from every channel in the application channel groups', async () => {
const prisma = createPrismaMock();
prisma.channelRouteRule.findMany.mockResolvedValue([
{
id: 'route-1', priority: 10,
group: {
id: 'group-1', name: '默认通道组',
items: [
{ channel: { id: 'channel-1', code: 'CH-1', name: '通道一', reportFields: [{ status: 'active', required: false, reportType: 'signature', drainageField: { id: 'field-1', code: 'license', name: '营业执照', fieldType: 'file', description: null, status: 'active' } }] } },
{ channel: { id: 'channel-2', code: 'CH-2', name: '通道二', reportFields: [{ status: 'active', required: true, reportType: 'both', drainageField: { id: 'field-1', code: 'license', name: '营业执照', fieldType: 'file', description: null, status: 'active' } }] } },
],
},
},
] as never);
const service = new SmsConfigService(prisma as never);
await expect(service.getApplicationReportFields('app-1', 'signature')).resolves.toEqual([
expect.objectContaining({
id: 'field-1',
code: 'license',
required: true,
reportTypes: ['signature', 'both'],
channels: [
expect.objectContaining({ id: 'channel-1', groupId: 'group-1' }),
expect.objectContaining({ id: 'channel-2', groupId: 'group-1' }),
],
}),
]);
});
it('validates and persists dynamic signature and drainage report values by channel', async () => {
const prisma = createPrismaMock();
prisma.smsSignature.findUnique.mockResolvedValue({ id: 'sig-1', tenantId: 'tenant-1', applicationId: 'app-1', auditStatus: 'draft' });
prisma.smsSignature.update.mockImplementation(({ data }) => Promise.resolve({ id: 'sig-1', tenantId: 'tenant-1', applicationId: 'app-1', ...data }));
prisma.channelRouteRule.findMany.mockResolvedValue([{
id: 'route-1', priority: 10,
group: {
id: 'group-1', name: '默认通道组',
items: [{
channel: {
id: 'channel-1', code: 'CH-1', name: '通道一',
reportFields: [
{ status: 'active', required: true, reportType: 'signature', drainageField: { id: 'field-1', code: 'license', name: '营业执照', fieldType: 'file', description: null, status: 'active' } },
{ status: 'active', required: true, reportType: 'drainage', drainageField: { id: 'field-2', code: 'site_owner', name: '网站主体', fieldType: 'text', description: null, status: 'active' } },
],
},
}],
},
}] as never);
const service = new SmsConfigService(prisma as never);
await service.updateSignature('sig-1', {
applicationId: 'app-1',
drainageInfo: {
signatureReportValues: { license: { fileObjectId: 'file-1', fileName: 'license.pdf' } },
links: [{ id: 'drain-1', reportValues: { site_owner: '企业A' } }],
},
});
expect(prisma.signatureReportMaterial.upsert).toHaveBeenCalledWith(expect.objectContaining({
create: expect.objectContaining({ signatureId: 'sig-1', channelId: 'channel-1', fieldCode: 'license', fileObjectId: 'file-1' }),
}));
expect(prisma.drainageReportMaterial.upsert).toHaveBeenCalledWith(expect.objectContaining({
create: expect.objectContaining({ signatureId: 'sig-1', drainageItemId: 'drain-1', channelId: 'channel-1', fieldCode: 'site_owner', fieldValue: '企业A' }),
}));
expect(prisma.drainageReportMaterial.deleteMany).toHaveBeenCalledWith({
where: { signatureId: 'sig-1', drainageItemId: { notIn: ['drain-1'] } },
});
});
it('updates enterprise signature drainage info through the admin API path', async () => {
const prisma = createPrismaMock();
const service = new SmsConfigService(prisma as never);