feat: add report material workflows and gateway safeguards

This commit is contained in:
hectorzhao
2026-07-15 18:23:48 +08:00
parent cf9f4ce4cd
commit 7091a8bed4
41 changed files with 3606 additions and 71 deletions
+25
View File
@@ -96,6 +96,7 @@ function createPrismaMock() {
},
drainageField: {
findUnique: jest.fn().mockResolvedValue({ id: 'library-1', code: 'license', name: '营业执照', fieldType: 'file', required: true, status: 'active', description: '执照文件' }),
findMany: jest.fn().mockResolvedValue([{ id: 'library-1', code: 'license', name: '营业执照', fieldType: 'file', required: true, status: 'active', description: '执照文件' }]),
},
signatureReportMaterial: {
findMany: jest.fn().mockResolvedValue([{ signatureId: 'sig-1', fieldCode: 'license', fieldValue: '营业执照', fileObjectId: 'file-1' }]),
@@ -178,6 +179,26 @@ describe('ChannelsService', () => {
});
});
it('replaces one report type while preserving legacy both fields for the opposite type', async () => {
const prisma = createPrismaMock();
const legacyBoth = { id: 'legacy-1', channelId: 'channel-1', drainageFieldId: 'library-1', reportType: 'both', code: 'license', name: '营业执照', exportName: '旧表头', fieldType: 'file', required: true, description: null, sortOrder: 10, columnWidth: 18, imageWidth: 120, imageHeight: 80, defaultValue: null, transform: null, status: 'active', createdAt: new Date(), updatedAt: new Date() };
const tx = {
channelReportField: {
findMany: jest.fn().mockResolvedValueOnce([legacyBoth]).mockResolvedValueOnce([]).mockResolvedValueOnce([{ id: 'signature-field' }]),
deleteMany: jest.fn(),
create: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: `created-${data.reportType}`, ...data })),
},
};
prisma.$transaction.mockImplementation((callback) => callback(tx));
const service = new ChannelsService(prisma as never);
await service.replaceReportFields('channel-1', 'signature', { fields: [{ drainageFieldId: 'library-1', exportName: '新签名表头', required: true }] });
expect(tx.channelReportField.deleteMany).toHaveBeenCalledWith({ where: { channelId: 'channel-1', reportType: { in: ['signature', 'both'] } } });
expect(tx.channelReportField.create).toHaveBeenCalledWith({ data: expect.objectContaining({ channelId: 'channel-1', code: 'license', reportType: 'drainage', exportName: '旧表头' }) });
expect(tx.channelReportField.create).toHaveBeenCalledWith({ data: expect.objectContaining({ channelId: 'channel-1', code: 'license', reportType: 'signature', exportName: '新签名表头' }) });
});
it('lists report tasks for one real channel', async () => {
const prisma = createPrismaMock();
prisma.channelSignatureReportTask.findMany.mockResolvedValue([]);
@@ -460,6 +481,7 @@ describe('ChannelsService', () => {
expect(prisma.smsChannelGroupItem.create).toHaveBeenCalledWith({
data: expect.objectContaining({ groupId: 'group-1', channelId: 'channel-1', carrier: 'mobile', province: '山东' }),
});
expect(prisma.smsChannelGroupItem.create.mock.calls[0][0].data).not.toHaveProperty('rateLimitPerSecond');
await expect(service.addGroupItem({ groupId: 'group-1', channelId: 'channel-1', carrier: 'unicom' }))
.rejects.toThrow('Channel group items must use the same carrier');
@@ -545,6 +567,9 @@ describe('ChannelsService', () => {
where: { id: 'group-1' },
data: expect.objectContaining({ retryTimeLimitHours: 13, retryTimeLimitMinutes: 750 }),
});
for (const item of tx.smsChannelGroupItem.createMany.mock.calls[0][0].data) {
expect(item).not.toHaveProperty('rateLimitPerSecond');
}
await expect(service.updateGroup('group-1', {
carrier: 'mobile',