feat: harden CMPP delivery and platform workflows

This commit is contained in:
hectorzhao
2026-07-20 18:07:29 +08:00
parent 80fb5a8f53
commit f02c33cbb7
61 changed files with 1834 additions and 281 deletions
+82
View File
@@ -0,0 +1,82 @@
import { PrismaService } from '../src/prisma/prisma.service';
import { ReportsService } from '../src/reports/reports.service';
const ids = {
tenant: 'it-report-tenant', application: 'it-report-app', channel: 'it-report-channel',
batch: 'it-report-batch', successMessage: 'it-report-message-success', failedMessage: 'it-report-message-failed',
};
async function main() {
const prisma = new PrismaService();
const reports = new ReportsService(prisma);
const queuedAt = new Date('2026-07-18T02:00:00.000Z');
try {
await prisma.tenant.create({ data: { id: ids.tenant, name: '报表集成验证企业', code: 'IT-REPORT-TENANT' } });
await prisma.smsApplication.create({ data: {
id: ids.application, tenantId: ids.tenant, name: '报表集成验证应用', cmppAccount: 'it-report-account',
cmppEnterpriseCode: 'ITREPORT', secretHash: 'not-a-real-secret',
} });
await prisma.smsChannel.create({ data: {
id: ids.channel, code: 'IT-REPORT-CHANNEL', name: '报表集成验证通道', gatewayHost: '127.0.0.1',
gatewayPort: 17890, account: 'it-report-upstream', passwordCipher: 'not-a-real-password', srcId: '10690000', unitPrice: 200n,
} });
await prisma.smsBatchTask.create({ data: {
id: ids.batch, tenantId: ids.tenant, applicationId: ids.application, taskNo: 'IT-REPORT-BATCH', sourceType: 'api',
content: '报表集成验证', phoneTotal: 2, status: 'finished', createdAt: queuedAt,
} });
const baseMessage = {
tenantId: ids.tenant, batchTaskId: ids.batch, applicationId: ids.application, content: '报表集成验证',
billingUnits: 1, unitPrice: 352n, amountCents: 352n, channelId: ids.channel, submittedAt: queuedAt, queuedAt,
};
await prisma.smsMessageRecord.createMany({ data: [
{ ...baseMessage, id: ids.successMessage, messageId: 'IT-REPORT-MSG-SUCCESS', phoneNumber: '13800000001', submitId: 'IT-REPORT-SUB-SUCCESS', gatewayMessageId: 'IT-REPORT-GW-SUCCESS', status: 'delivered', submitStatus: 'accepted', receiptStatus: 'delivered', receiptRawStatus: 'DELIVRD', deliveredAt: new Date(queuedAt.getTime() + 5_000) },
{ ...baseMessage, id: ids.failedMessage, messageId: 'IT-REPORT-MSG-FAILED', phoneNumber: '13800000002', submitId: 'IT-REPORT-SUB-FAILED', gatewayMessageId: 'IT-REPORT-GW-FAILED', status: 'failed', submitStatus: 'accepted', receiptStatus: 'undelivered', receiptRawStatus: 'UNDELIV', deliveredAt: new Date(queuedAt.getTime() + 8_000) },
] });
await prisma.smsSubmitRecord.createMany({ data: [
{ id: 'it-report-submit-success', tenantId: ids.tenant, batchTaskId: ids.batch, messageRecordId: ids.successMessage, channelId: ids.channel, submitId: 'IT-REPORT-SUB-SUCCESS', gatewayMessageId: 'IT-REPORT-GW-SUCCESS', submitStatus: 'accepted', costUnitPrice: 200n, costAmountCents: 200n, submittedAt: queuedAt },
{ id: 'it-report-submit-failed', tenantId: ids.tenant, batchTaskId: ids.batch, messageRecordId: ids.failedMessage, channelId: ids.channel, submitId: 'IT-REPORT-SUB-FAILED', gatewayMessageId: 'IT-REPORT-GW-FAILED', submitStatus: 'accepted', costUnitPrice: 200n, costAmountCents: 200n, submittedAt: queuedAt },
] });
await prisma.smsReceiptRecord.createMany({ data: [
{ id: 'it-report-receipt-success', receiptKey: 'it-report-receipt-key-success', tenantId: ids.tenant, batchTaskId: ids.batch, messageRecordId: ids.successMessage, channelId: ids.channel, messageId: 'IT-REPORT-MSG-SUCCESS', gatewayMessageId: 'IT-REPORT-GW-SUCCESS', receiptStatus: 'delivered', rawStatus: 'DELIVRD', deliveredAt: new Date(queuedAt.getTime() + 5_000) },
{ id: 'it-report-receipt-failed', receiptKey: 'it-report-receipt-key-failed', tenantId: ids.tenant, batchTaskId: ids.batch, messageRecordId: ids.failedMessage, channelId: ids.channel, messageId: 'IT-REPORT-MSG-FAILED', gatewayMessageId: 'IT-REPORT-GW-FAILED', receiptStatus: 'undelivered', rawStatus: 'UNDELIV', deliveredAt: new Date(queuedAt.getTime() + 8_000) },
] });
await prisma.smsBillingRecord.createMany({ data: [
{ id: 'it-report-billing-success', tenantId: ids.tenant, applicationId: ids.application, taskId: ids.batch, messageId: 'IT-REPORT-MSG-SUCCESS', phoneNumber: '13800000001', contentLength: 8, billingUnits: 1, unitPrice: 352n, amountCents: 352n, billingStatus: 'charged', createdAt: queuedAt },
{ id: 'it-report-billing-refunded', tenantId: ids.tenant, applicationId: ids.application, taskId: ids.batch, messageId: 'IT-REPORT-MSG-FAILED', phoneNumber: '13800000002', contentLength: 8, billingUnits: 1, unitPrice: 352n, amountCents: 352n, billingStatus: 'refunded', createdAt: queuedAt },
] });
await reports.refreshRollingWindow(new Date('2026-07-20T05:00:00.000Z'));
const [reconciliation, applicationProfit, channelProfit, applicationQuality, channelQuality] = await Promise.all([
prisma.dailyReconciliationReport.findUnique({ where: { reportDate_tenantId_applicationId: { reportDate: new Date('2026-07-18'), tenantId: ids.tenant, applicationId: ids.application } } }),
prisma.dailyProfitReport.findUnique({ where: { reportDate_dimensionType_dimensionId: { reportDate: new Date('2026-07-18'), dimensionType: 'application', dimensionId: ids.application } } }),
prisma.dailyProfitReport.findUnique({ where: { reportDate_dimensionType_dimensionId: { reportDate: new Date('2026-07-18'), dimensionType: 'channel', dimensionId: ids.channel } } }),
prisma.dailyQualityReport.findUnique({ where: { reportDate_dimensionType_dimensionId: { reportDate: new Date('2026-07-18'), dimensionType: 'application', dimensionId: ids.application } } }),
prisma.dailyQualityReport.findUnique({ where: { reportDate_dimensionType_dimensionId: { reportDate: new Date('2026-07-18'), dimensionType: 'channel', dimensionId: ids.channel } } }),
]);
const evidence = JSON.parse(JSON.stringify({ reconciliation, applicationProfit, channelProfit, applicationQuality, channelQuality }, (_key, value) => typeof value === 'bigint' ? value.toString() : value));
const valid = reconciliation?.sentUnits === 2 && reconciliation.successUnits === 1 && reconciliation.failedUnits === 1
&& applicationProfit?.revenueCents === 352n && applicationProfit.refundCents === 352n && applicationProfit.costCents === 400n && applicationProfit.profitCents === -48n
&& channelProfit?.successUnits === 1 && channelProfit.failedUnits === 1
&& applicationQuality?.avgArrivalMs === 5_000 && channelQuality?.avgArrivalMs === 5_000;
if (!valid) throw new Error(`report recalculation assertion failed: ${JSON.stringify(evidence)}`);
console.log(JSON.stringify({ ok: true, evidence }));
} finally {
await prisma.dailyQualityReport.deleteMany({ where: { OR: [{ tenantId: ids.tenant }, { channelId: ids.channel }] } });
await prisma.dailyProfitReport.deleteMany({ where: { OR: [{ tenantId: ids.tenant }, { channelId: ids.channel }] } });
await prisma.dailyReconciliationReport.deleteMany({ where: { tenantId: ids.tenant } });
await prisma.smsBillingRecord.deleteMany({ where: { tenantId: ids.tenant } });
await prisma.smsReceiptRecord.deleteMany({ where: { tenantId: ids.tenant } });
await prisma.smsSubmitRecord.deleteMany({ where: { tenantId: ids.tenant } });
await prisma.smsMessageRecord.deleteMany({ where: { tenantId: ids.tenant } });
await prisma.smsBatchTask.deleteMany({ where: { tenantId: ids.tenant } });
await prisma.smsChannel.deleteMany({ where: { id: ids.channel } });
await prisma.smsApplication.deleteMany({ where: { id: ids.application } });
await prisma.tenant.deleteMany({ where: { id: ids.tenant } });
await prisma.$disconnect();
}
}
void main().catch((error) => {
console.error(error);
process.exitCode = 1;
});