fix: polish channel groups and add production deployment

This commit is contained in:
hectorzhao
2026-07-07 11:16:08 +08:00
parent b5132d7f4e
commit 72f2c010ce
44 changed files with 1265 additions and 489 deletions
+50 -1
View File
@@ -49,6 +49,7 @@ function createPrismaMock() {
status: 'active',
retryEnabled: true,
retryTimeLimitHours: 72,
retryTimeLimitMinutes: 4320,
items: [{ id: 'item-1', groupId: 'group-1', channelId: 'channel-1', carrier: 'mobile', priority: 1, province: null, channel }],
},
};
@@ -379,7 +380,7 @@ describe('SendChainService', () => {
applicationId: 'app-1',
groupId: 'group-1',
carrier: 'mobile',
group: { id: 'group-1', carrier: 'mobile', status: 'active', retryEnabled: false, retryTimeLimitHours: 72, items: [] },
group: { id: 'group-1', carrier: 'mobile', status: 'active', retryEnabled: false, retryTimeLimitHours: 72, retryTimeLimitMinutes: 4320, items: [] },
});
await service.handleSubmitResult({
@@ -400,6 +401,53 @@ describe('SendChainService', () => {
expect(billing.refund).toHaveBeenCalledWith(expect.objectContaining({ remark: '最终失败退款' }));
});
it('stops failed receipt retry after the configured minute limit', async () => {
const { service, prisma, billing } = createService();
prisma.smsBillingRecord.findFirst
.mockResolvedValueOnce(null)
.mockResolvedValueOnce({ id: 'bill-1', billingStatus: 'charged' });
prisma.smsMessageRecord.findFirst.mockResolvedValue({
id: 'record-1',
tenantId: 'tenant-1',
batchTaskId: 'task-1',
applicationId: 'app-1',
templateId: 'tpl-1',
messageId: 'MSG-1',
phoneNumber: '13800000001',
content: 'hello',
channelId: 'channel-1',
gatewayMessageId: 'GW-1',
status: 'submitted',
amountCents: 3,
billingUnits: 1,
unitPrice: 3,
queuedAt: new Date(Date.now() - 90 * 60_000),
});
prisma.channelRouteRule.findFirst.mockResolvedValue({
id: 'route-1',
tenantId: 'tenant-1',
applicationId: 'app-1',
groupId: 'group-1',
carrier: 'mobile',
group: { id: 'group-1', carrier: 'mobile', status: 'active', retryEnabled: true, retryTimeLimitHours: 2, retryTimeLimitMinutes: 75, items: [] },
});
await service.handleReceipt({
messageId: 'MSG-1',
channelId: 'channel-1',
gatewayMessageId: 'GW-1',
receiptStatus: 'undelivered',
rawStatus: 'UNDELIV',
});
expect(prisma.smsSubmitRecord.create).not.toHaveBeenCalled();
expect(billing.refund).toHaveBeenCalledWith(expect.objectContaining({ remark: '最终失败退款' }));
expect(prisma.smsMessageRecord.update).toHaveBeenCalledWith({
where: { id: 'record-1' },
data: expect.objectContaining({ status: 'failed', receiptStatus: 'undelivered' }),
});
});
it('does not let stale failed receipts overwrite a later delivered message', async () => {
const { service, prisma, billing } = createService();
prisma.smsMessageRecord.findFirst.mockResolvedValue({
@@ -467,6 +515,7 @@ describe('SendChainService', () => {
status: 'active',
retryEnabled: true,
retryTimeLimitHours: 72,
retryTimeLimitMinutes: 4320,
items: [
{
id: 'wrong-item',