132 lines
4.4 KiB
TypeScript
132 lines
4.4 KiB
TypeScript
import type { PrismaService } from '../prisma/prisma.service';
|
|
|
|
export type FinalReceiptMessage = {
|
|
id: string;
|
|
tenantId?: string | null;
|
|
applicationId?: string | null;
|
|
messageId: string;
|
|
phoneNumber: string;
|
|
cmppSubmitSequenceId?: string | null;
|
|
cmppSubmitGroupMessageId?: string | null;
|
|
cmppRegisteredDelivery?: boolean | null;
|
|
};
|
|
|
|
export type DownstreamDeliveryQueueRequest = {
|
|
tenantId: string;
|
|
applicationId?: string | null;
|
|
messageRecordId?: string | null;
|
|
messageId?: string | null;
|
|
deliveryType: 'receipt' | 'uplink';
|
|
payload: Record<string, unknown>;
|
|
receiptDedupeKey?: string;
|
|
queueHttpWebhook?: boolean;
|
|
queueCmppDelivery?: boolean;
|
|
propagateHttpQueueError?: boolean;
|
|
};
|
|
|
|
type ClientReceiptTarget = {
|
|
segmentIndex: number;
|
|
segmentTotal: number;
|
|
submitSequenceId: number;
|
|
submitGroupMessageId: string;
|
|
registeredDelivery: boolean;
|
|
};
|
|
|
|
async function resolveClientReceiptTargets(
|
|
prisma: PrismaService,
|
|
message: FinalReceiptMessage,
|
|
): Promise<ClientReceiptTarget[]> {
|
|
if (message.cmppSubmitGroupMessageId) {
|
|
const group = await prisma.cmppInboundLongMessage.findFirst({
|
|
where: { messageId: message.cmppSubmitGroupMessageId },
|
|
include: { segments: { orderBy: { segmentIndex: 'asc' } } },
|
|
});
|
|
if (group?.segments.length) {
|
|
return group.segments.flatMap((segment) => {
|
|
const submitSequenceId = Number(segment.sequenceId);
|
|
if (!Number.isInteger(submitSequenceId) || submitSequenceId <= 0) return [];
|
|
return [{
|
|
segmentIndex: segment.segmentIndex,
|
|
segmentTotal: group.segmentTotal,
|
|
submitSequenceId,
|
|
submitGroupMessageId: group.messageId,
|
|
registeredDelivery: segment.registeredDelivery,
|
|
}];
|
|
});
|
|
}
|
|
}
|
|
|
|
const submitSequenceId = Number(message.cmppSubmitSequenceId);
|
|
if (!Number.isInteger(submitSequenceId) || submitSequenceId <= 0) return [];
|
|
return [{
|
|
segmentIndex: 1,
|
|
segmentTotal: 1,
|
|
submitSequenceId,
|
|
submitGroupMessageId: message.cmppSubmitGroupMessageId ?? message.messageId,
|
|
// Null means a historical CMPP record created before this field existed.
|
|
registeredDelivery: message.cmppRegisteredDelivery !== false,
|
|
}];
|
|
}
|
|
|
|
/**
|
|
* Queue one business-level HTTP callback and one CMPP status report for every
|
|
* original client fragment that requested Registered_Delivery. Internal retry,
|
|
* refund and billing remain message-level; only protocol delivery is expanded.
|
|
*/
|
|
export async function queueFinalReceiptDeliveries(
|
|
prisma: PrismaService,
|
|
queue: (request: DownstreamDeliveryQueueRequest) => Promise<unknown>,
|
|
data: {
|
|
message: FinalReceiptMessage;
|
|
payload: Record<string, unknown>;
|
|
segmentPayloads?: Record<number, Record<string, unknown>>;
|
|
propagateHttpQueueError?: boolean;
|
|
},
|
|
) {
|
|
const { message } = data;
|
|
if (!message.tenantId || !message.applicationId) {
|
|
return { queued: false, cmppTargetCount: 0 };
|
|
}
|
|
|
|
// HTTP submissions have one client message identity, so their webhook stays
|
|
// message-level even when the carrier internally split the SMS into segments.
|
|
await queue({
|
|
tenantId: message.tenantId,
|
|
applicationId: message.applicationId,
|
|
messageRecordId: message.id,
|
|
messageId: message.messageId,
|
|
deliveryType: 'receipt',
|
|
payload: data.payload,
|
|
queueHttpWebhook: true,
|
|
queueCmppDelivery: false,
|
|
propagateHttpQueueError: data.propagateHttpQueueError,
|
|
});
|
|
|
|
const targets = (await resolveClientReceiptTargets(prisma, message))
|
|
.filter((target) => target.registeredDelivery);
|
|
for (const target of targets) {
|
|
const isSingleFragment = target.segmentTotal === 1;
|
|
await queue({
|
|
tenantId: message.tenantId,
|
|
applicationId: message.applicationId,
|
|
messageRecordId: message.id,
|
|
messageId: message.messageId,
|
|
deliveryType: 'receipt',
|
|
payload: {
|
|
...data.payload,
|
|
...data.segmentPayloads?.[target.segmentIndex],
|
|
submitSequenceId: target.submitSequenceId,
|
|
submitGroupMessageId: target.submitGroupMessageId,
|
|
clientSegmentIndex: target.segmentIndex,
|
|
clientSegmentTotal: target.segmentTotal,
|
|
},
|
|
receiptDedupeKey: isSingleFragment
|
|
? `receipt:${message.id}`
|
|
: `receipt:${message.id}:segment:${target.segmentIndex}`,
|
|
queueHttpWebhook: false,
|
|
queueCmppDelivery: true,
|
|
});
|
|
}
|
|
return { queued: true, cmppTargetCount: targets.length };
|
|
}
|