feat: add reconciliation and quality reporting
This commit is contained in:
@@ -282,6 +282,7 @@ export class SendChainService implements OnModuleInit, OnModuleDestroy {
|
||||
const phones = [...new Set(data.phones ?? [])];
|
||||
const schedule = parseSchedule(data);
|
||||
await this.validateSendResources(data.tenantId, data.applicationId, data.templateId);
|
||||
const messageClassification = await this.resolveTemplateMessageClassification(data.templateId, data.content);
|
||||
const unitPrice = await this.resolveUnitPrice(data.tenantId, data.applicationId);
|
||||
const queuePriority = await this.resolveQueuePriority(data.tenantId, data.applicationId);
|
||||
const risk = await this.riskReview.evaluateTask({
|
||||
@@ -366,6 +367,8 @@ export class SendChainService implements OnModuleInit, OnModuleDestroy {
|
||||
batchTaskId: task.id,
|
||||
applicationId: data.applicationId,
|
||||
templateId: data.templateId,
|
||||
signatureId: messageClassification.signatureId,
|
||||
drainageInfoId: messageClassification.drainageInfoId,
|
||||
messageId: `MSG-${randomUUID()}`,
|
||||
phoneNumber: phone,
|
||||
content: data.content,
|
||||
@@ -750,7 +753,7 @@ export class SendChainService implements OnModuleInit, OnModuleDestroy {
|
||||
: null;
|
||||
const submittedAt = data.submittedAt ? new Date(data.submittedAt) : new Date();
|
||||
await this.prisma.smsSubmitRecord.updateMany({
|
||||
where: { OR: [{ submitId: data.submitId }, { messageRecordId: message.id }] },
|
||||
where: data.submitId ? { submitId: data.submitId } : { messageRecordId: message.id },
|
||||
data: {
|
||||
sequenceId: data.sequenceId,
|
||||
gatewayMessageId: data.gatewayMessageId,
|
||||
@@ -1744,6 +1747,7 @@ export class SendChainService implements OnModuleInit, OnModuleDestroy {
|
||||
await this.recordCmppFailureReceipt(message, code, reason);
|
||||
};
|
||||
const queueAfterRiskChecks = async (options: { templateId?: string; signatureId?: string }) => {
|
||||
const drainageInfoId = await this.resolveDrainageInfoId(options.signatureId, data.content);
|
||||
const risk = await this.riskReview.evaluateTask({
|
||||
tenantId: application.tenantId,
|
||||
applicationId: application.id,
|
||||
@@ -1759,7 +1763,7 @@ export class SendChainService implements OnModuleInit, OnModuleDestroy {
|
||||
if (risk.status === 'pending_review') {
|
||||
await this.prisma.smsMessageRecord.update({
|
||||
where: { id: message.id },
|
||||
data: { status: 'pending_review', signatureId: options.signatureId },
|
||||
data: { status: 'pending_review', signatureId: options.signatureId, drainageInfoId },
|
||||
});
|
||||
await this.prisma.smsBatchTask.update({
|
||||
where: { id: task.id },
|
||||
@@ -1786,7 +1790,7 @@ export class SendChainService implements OnModuleInit, OnModuleDestroy {
|
||||
}
|
||||
await this.prisma.smsMessageRecord.update({
|
||||
where: { id: message.id },
|
||||
data: { status: 'queued', signatureId: options.signatureId },
|
||||
data: { status: 'queued', signatureId: options.signatureId, drainageInfoId },
|
||||
});
|
||||
await this.prisma.smsBatchTask.update({
|
||||
where: { id: task.id },
|
||||
@@ -1831,7 +1835,7 @@ export class SendChainService implements OnModuleInit, OnModuleDestroy {
|
||||
});
|
||||
}
|
||||
const reviewTask = risk.status === 'pending_review' && risk.task
|
||||
? await this.attachMessageToReviewTask(risk.task.id, message.id, signature.id)
|
||||
? await this.attachMessageToReviewTask(risk.task.id, message.id, signature.id, await this.resolveDrainageInfoId(signature.id, data.content))
|
||||
: await this.riskReview.aggregateTemplateMismatch({
|
||||
tenantId: application.tenantId,
|
||||
applicationId: application.id,
|
||||
@@ -1986,6 +1990,8 @@ export class SendChainService implements OnModuleInit, OnModuleDestroy {
|
||||
sessionId: session.id,
|
||||
submitId,
|
||||
submitStatus: 'queued',
|
||||
costUnitPrice: channel.unitPrice ?? 0,
|
||||
costAmountCents: (channel.unitPrice ?? 0) * Math.max(1, message.billingUnits ?? 1),
|
||||
},
|
||||
});
|
||||
await this.prisma.smsMessageRecord.update({
|
||||
@@ -2276,10 +2282,37 @@ export class SendChainService implements OnModuleInit, OnModuleDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
private async attachMessageToReviewTask(reviewTaskId: string, messageRecordId: string, signatureId: string) {
|
||||
private async resolveTemplateMessageClassification(templateId: string | undefined, content: string) {
|
||||
if (!templateId) return { signatureId: undefined, drainageInfoId: undefined };
|
||||
const template = await this.prisma.smsTemplate.findUnique({
|
||||
where: { id: templateId },
|
||||
select: { signatureId: true },
|
||||
});
|
||||
const signatureId = template?.signatureId ?? undefined;
|
||||
return { signatureId, drainageInfoId: await this.resolveDrainageInfoId(signatureId, content) };
|
||||
}
|
||||
|
||||
private async resolveDrainageInfoId(signatureId: string | undefined, content: string) {
|
||||
if (!signatureId) return undefined;
|
||||
const candidates = await this.prisma.smsDrainageInfo.findMany({
|
||||
where: { signatureId, auditStatus: 'approved' },
|
||||
select: { id: true, url: true, updatedAt: true },
|
||||
orderBy: [{ updatedAt: 'desc' }, { id: 'asc' }],
|
||||
});
|
||||
const matches = candidates
|
||||
.map((item) => ({ ...item, normalizedUrl: item.url.trim() }))
|
||||
.filter((item) => item.normalizedUrl.length > 0 && content.includes(item.normalizedUrl))
|
||||
.sort((left, right) => right.normalizedUrl.length - left.normalizedUrl.length || right.updatedAt.getTime() - left.updatedAt.getTime());
|
||||
if (matches.length === 0) return undefined;
|
||||
const longestLength = matches[0].normalizedUrl.length;
|
||||
const longestMatches = matches.filter((item) => item.normalizedUrl.length === longestLength);
|
||||
return longestMatches.length === 1 ? longestMatches[0].id : undefined;
|
||||
}
|
||||
|
||||
private async attachMessageToReviewTask(reviewTaskId: string, messageRecordId: string, signatureId: string, drainageInfoId?: string) {
|
||||
await this.prisma.smsMessageRecord.update({
|
||||
where: { id: messageRecordId },
|
||||
data: { reviewTaskId, signatureId, status: 'pending_review' },
|
||||
data: { reviewTaskId, signatureId, drainageInfoId, status: 'pending_review' },
|
||||
});
|
||||
return this.prisma.smsSendTask.findUnique({ where: { id: reviewTaskId } });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user