fix: 修复 CMPP 协议字段容量与版本兼容性
CSS quality / css-quality (push) Has been cancelled

This commit is contained in:
hectorzhao
2026-09-20 15:49:37 +08:00
parent b24cd7c08d
commit 001d5f2cbd
37 changed files with 1933 additions and 295 deletions
+47 -33
View File
@@ -1,3 +1,4 @@
import { protocolUint32ToDb, protocolUint32FromDb } from '../common/protocol-uint32';
import { resolveReceiptAttempt } from './receipt-attempt-resolver';
import { completionContext } from './completion-context';
import { Logger, NotFoundException } from '@nestjs/common';
@@ -39,6 +40,7 @@ export class SendReceiptService {
) {}
async intakeReceipt(data: GatewayReceiptEventDto) {
protocolUint32ToDb(data.sequenceId);
const channel = await this.prisma.smsChannel.findUnique({
where: { id: data.channelId },
select: {
@@ -55,34 +57,45 @@ export class SendReceiptService {
}
const deliveredAt = data.deliveredAt ? new Date(data.deliveredAt) : new Date();
const receiptKey = receiptEventKey(data, data.channelId);
const inbox = await this.prisma.upstreamReceiptInbox.upsert({
where: { receiptKey },
update: {
incomingConnectionId: data.connectionId,
},
create: {
receiptKey,
incomingChannelId: data.channelId,
incomingConnectionId: data.connectionId,
upstreamAccount: channel.account,
upstreamHost: channel.gatewayHost,
upstreamPort: channel.gatewayPort,
protocol: channel.protocol,
protocolVersion: channel.cmppVersion,
provisionalMessageId: data.messageId,
sequenceId: data.sequenceId,
gatewayMessageId: data.gatewayMessageId,
phoneNumber: data.phoneNumber?.trim() || null,
receiptStatus: data.receiptStatus,
rawStatus: data.rawStatus,
errorCode: data.errorCode,
errorMessage: data.errorMessage,
deliveredAt,
gatewayReceivedAt: data.deliveredAt ? new Date(data.deliveredAt) : null,
status: 'pending',
nextRetryAt: new Date(),
},
});
const inbox = await this.prisma.upstreamReceiptInbox
.upsert({
where: { receiptKey },
update: {
incomingConnectionId: data.connectionId,
},
create: {
receiptKey,
incomingChannelId: data.channelId,
incomingConnectionId: data.connectionId,
upstreamAccount: channel.account,
upstreamHost: channel.gatewayHost,
upstreamPort: channel.gatewayPort,
protocol: channel.protocol,
protocolVersion: channel.cmppVersion,
provisionalMessageId: data.messageId,
sequenceId: protocolUint32ToDb(data.sequenceId),
gatewayMessageId: data.gatewayMessageId,
phoneNumber: data.phoneNumber?.trim() || null,
receiptStatus: data.receiptStatus,
rawStatus: data.rawStatus,
errorCode: data.errorCode,
errorMessage: data.errorMessage,
deliveredAt,
gatewayReceivedAt: data.deliveredAt ? new Date(data.deliveredAt) : null,
status: 'pending',
nextRetryAt: new Date(),
},
})
.catch(async (error: unknown) => {
// Prisma can emulate an upsert when the optional update is empty. Another
// callback may win the unique receiptKey insert; acknowledge only the
// exact durable fact, never swallow unrelated persistence failures.
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002') {
const existing = await this.prisma.upstreamReceiptInbox.findUnique({ where: { receiptKey } });
if (existing) return existing;
}
throw error;
});
if (['pending', 'retrying'].includes(inbox.status)) {
setImmediate(() => void this.facade.processUpstreamReceiptInboxRecord(inbox.id));
}
@@ -143,7 +156,7 @@ export class SendReceiptService {
messageId: inbox.provisionalMessageId ?? undefined,
channelId: inbox.incomingChannelId,
connectionId: inbox.incomingConnectionId ?? undefined,
sequenceId: inbox.sequenceId ?? undefined,
sequenceId: protocolUint32FromDb(inbox.sequenceId),
gatewayMessageId: inbox.gatewayMessageId,
phoneNumber: inbox.phoneNumber ?? undefined,
receiptStatus: normalizeReceiptStatus(inbox.receiptStatus),
@@ -223,6 +236,7 @@ export class SendReceiptService {
cmppVersion: string;
},
) {
protocolUint32ToDb(data.sequenceId);
const resolved = await this.facade.resolveReceiptMessage(data, incomingIdentity);
const logicalChannelId = resolved.channelId ?? data.channelId;
const receiptKey = receiptEventKey(data, logicalChannelId);
@@ -245,7 +259,7 @@ export class SendReceiptService {
},
data: {
gatewayMessageId: data.gatewayMessageId,
sequenceId: data.sequenceId,
sequenceId: protocolUint32ToDb(data.sequenceId),
},
});
}
@@ -262,7 +276,7 @@ export class SendReceiptService {
messageId: resolved.messageId,
gatewayMessageId: data.gatewayMessageId,
phoneNumber: data.phoneNumber?.trim() || message.phoneNumber,
sequenceId: data.sequenceId,
sequenceId: protocolUint32ToDb(data.sequenceId),
receiptStatus: data.receiptStatus,
rawStatus: data.rawStatus,
errorCode: data.errorCode,
@@ -573,7 +587,7 @@ export class SendReceiptService {
update: {
submitRecordId: submitRecord?.id ?? submitRecordId ?? null,
channelId: data.channelId ?? message.channelId ?? null,
sequenceId: data.sequenceId ?? null,
sequenceId: protocolUint32ToDb(data.sequenceId) ?? null,
gatewayMessageId: data.gatewayMessageId,
receiptStatus: data.receiptStatus,
rawStatus: data.rawStatus,
@@ -590,7 +604,7 @@ export class SendReceiptService {
attempt: 0,
segmentTotal: Math.max(1, Number(message.billingUnits ?? 1)),
segmentIndex: 1,
sequenceId: data.sequenceId ?? null,
sequenceId: protocolUint32ToDb(data.sequenceId) ?? null,
gatewayMessageId: data.gatewayMessageId,
submitStatus: submitRecord?.submitStatus ?? 'accepted',
receiptStatus: data.receiptStatus,