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
+35 -2
View File
@@ -224,9 +224,12 @@ function createPrismaMock() {
findUnique: jest.fn().mockResolvedValue({
id: 'dead-1',
tenantId: 'tenant-1',
channelId: 'channel-1',
streamMessageId: '1710000000000-0',
submitId: 'SUB-1',
messageId: 'MSG-1',
status: 'pending',
manualRetryCount: 0,
commandPayload: {
schemaVersion: 'v1',
messageType: 'SubmitCommand',
@@ -1517,7 +1520,7 @@ describe('SendChainService', () => {
});
});
it('records gateway submit dead letters and allows manual requeue', async () => {
it('records gateway submit exceptions and safely allows manual requeue', async () => {
const { service, prisma } = createService();
service['publishGatewaySubmitCommand'] = jest.fn().mockResolvedValue('1710000001000-0');
@@ -1551,9 +1554,17 @@ describe('SendChainService', () => {
}),
});
await service.requeueGatewaySubmitDeadLetter('dead-1');
await service.requeueGatewaySubmitDeadLetter('dead-1', {
confirmedNotSubmitted: true,
reason: '确认通道连接失败且运营商未收到该短信',
operatorId: 'user-1',
});
expect(service['publishGatewaySubmitCommand']).toHaveBeenCalledWith(expect.objectContaining({ submitId: 'SUB-1' }));
expect(prisma.gatewaySubmitDeadLetter.updateMany).toHaveBeenCalledWith({
where: { id: 'dead-1', status: 'pending' },
data: { status: 'requeueing' },
});
expect(prisma.gatewaySubmitDeadLetter.update).toHaveBeenCalledWith({
where: { id: 'dead-1' },
data: expect.objectContaining({
@@ -1567,10 +1578,32 @@ describe('SendChainService', () => {
action: 'gateway.submit_dead_letter_requeue',
resource: 'gateway_submit_dead_letter',
resourceId: 'dead-1',
userId: 'user-1',
detail: expect.objectContaining({
reason: '确认通道连接失败且运营商未收到该短信',
confirmedNotSubmitted: true,
}),
}),
});
});
it('blocks submit exception requeue when the upstream result may already be accepted', async () => {
const { service, prisma } = createService();
prisma.smsMessageRecord.findUnique.mockResolvedValueOnce({
id: 'record-1',
messageId: 'MSG-1',
status: 'submitted',
submitStatus: 'accepted',
receiptStatus: null,
});
await expect(service.requeueGatewaySubmitDeadLetter('dead-1', {
confirmedNotSubmitted: true,
reason: '尝试重新发送这条短信',
})).rejects.toThrow('为避免重复发送,禁止重新入队');
expect(service['publishGatewaySubmitCommand']).not.toHaveBeenCalled();
});
it('records gateway downstream recovery statuses', async () => {
const { service, prisma } = createService();