feat: add reconciliation and quality reporting

This commit is contained in:
hectorzhao
2026-07-15 14:22:50 +08:00
parent 16311546af
commit 8c3336600e
32 changed files with 1730 additions and 200 deletions
+26 -1
View File
@@ -99,6 +99,9 @@ function createPrismaMock() {
smsSignature: {
findFirst: jest.fn().mockResolvedValue({ id: 'sig-1', name: '签名', auditStatus: 'approved', reportStatus: 'reporting' }),
},
smsDrainageInfo: {
findMany: jest.fn().mockResolvedValue([]),
},
smsSendTask: {
findUnique: jest.fn().mockResolvedValue(null),
},
@@ -348,6 +351,28 @@ describe('SendChainService', () => {
expect(service.enqueueBatchTask).toHaveBeenCalledWith('task-1');
});
it('persists the unique longest approved drainage URL match on new message records', async () => {
const { service, prisma } = createService();
service.enqueueBatchTask = jest.fn().mockResolvedValue({ taskId: 'task-1', enqueued: 1 });
prisma.smsTemplate.findUnique.mockResolvedValue({
id: 'tpl-1', tenantId: 'tenant-1', applicationId: 'app-1', signatureId: 'sig-1', auditStatus: 'approved',
signature: { id: 'sig-1', auditStatus: 'approved', reportStatus: 'reporting' },
});
prisma.smsDrainageInfo.findMany.mockResolvedValue([
{ id: 'drain-short', url: 'https://a.example', updatedAt: new Date('2026-07-01') },
{ id: 'drain-long', url: 'https://a.example/landing', updatedAt: new Date('2026-07-02') },
]);
await service.createBatchTask({
tenantId: 'tenant-1', applicationId: 'app-1', templateId: 'tpl-1',
content: '详情请访问 https://a.example/landing', phones: ['13800000001'],
});
expect(prisma.smsMessageRecord.createMany).toHaveBeenCalledWith({
data: [expect.objectContaining({ signatureId: 'sig-1', drainageInfoId: 'drain-long' })],
});
});
it('creates scheduled tasks without immediate enqueue and dispatches due tasks later', async () => {
const { service, prisma, billing } = createService();
service.enqueueBatchTask = jest.fn().mockResolvedValue({ taskId: 'task-1', enqueued: 1 });
@@ -934,7 +959,7 @@ describe('SendChainService', () => {
});
expect(prisma.smsSubmitRecord.updateMany).toHaveBeenCalledWith({
where: { OR: [{ submitId: 'SUB-1' }, { messageRecordId: 'record-1' }] },
where: { submitId: 'SUB-1' },
data: expect.objectContaining({ sequenceId: 7, gatewayMessageId: 'GW-1', submitStatus: 'accepted' }),
});
expect(prisma.smsMessageRecord.update).toHaveBeenCalledWith({