import { createHash } from 'node:crypto'; import { PrismaPg } from '../../api/node_modules/@prisma/adapter-pg/dist/index.js'; import { PrismaClient } from '../../api/node_modules/@prisma/client/index.js'; const databaseUrl = process.env.DATABASE_URL ?? 'postgresql://cmpp:cmpp_password@localhost:5432/cmpp_platform?schema=public'; const prisma = new PrismaClient({ adapter: new PrismaPg(databaseUrl) }); const PREFIX = 'screenshot-seed-20260822'; const tenantId = `${PREFIX}-tenant`; const applicationIds = [`${PREFIX}-app-notice`, `${PREFIX}-app-marketing`]; const channelIds = [`${PREFIX}-channel-mobile`, `${PREFIX}-channel-unicom`, `${PREFIX}-channel-telecom`]; const signatureIds = [`${PREFIX}-signature-service`, `${PREFIX}-signature-member`, `${PREFIX}-signature-cloud`]; const drainageIds = [`${PREFIX}-drainage-mall`, `${PREFIX}-drainage-event`]; const tenantName = '星河智联科技有限公司'; const applicationNames = ['星河通知中心', '星河会员营销']; const channelNames = ['华东移动一号通道', '华北联通优质通道', '全国电信高速通道']; const signatureNames = ['【星河服务】', '【星河会员】', '【星河云】']; const now = new Date(); const dayMs = 86_400_000; function id(suffix) { return `${PREFIX}-${suffix}`; } function sha256(value) { return createHash('sha256').update(value).digest('hex'); } function dateDaysAgo(days, hour = 10, minute = 0) { const value = new Date(now.getTime() - days * dayMs); value.setHours(hour, minute, 0, 0); return value; } function dateKeyDaysAgo(days) { const value = dateDaysAgo(days, 0, 0); return `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, '0')}-${String(value.getDate()).padStart(2, '0')}`; } function reportDate(days) { return new Date(`${dateKeyDaysAgo(days)}T00:00:00.000Z`); } async function clearPreviousSeed() { await prisma.protocolInteractionLog.deleteMany({ where: { id: { startsWith: PREFIX } } }); await prisma.smsMessageRecord.deleteMany({ where: { id: { startsWith: PREFIX } } }); await prisma.smsBatchTask.deleteMany({ where: { id: { startsWith: PREFIX } } }); await prisma.smsSendTask.deleteMany({ where: { id: { startsWith: PREFIX } } }); await prisma.dailyReconciliationReport.deleteMany({ where: { id: { startsWith: PREFIX } } }); await prisma.dailyProfitReport.deleteMany({ where: { id: { startsWith: PREFIX } } }); await prisma.dailyQualityReport.deleteMany({ where: { id: { startsWith: PREFIX } } }); } async function ensureDimensions() { await prisma.tenant.upsert({ where: { code: 'SCREENSHOT-DEMO' }, update: { name: tenantName, status: 'active', certificationStatus: 'approved' }, create: { id: tenantId, code: 'SCREENSHOT-DEMO', name: tenantName, status: 'active', certificationStatus: 'approved' }, }); await prisma.tenantAccount.upsert({ where: { tenantId }, update: { balanceCents: 268_560_00n, creditCents: 50_000_00n, status: 'active' }, create: { id: id('tenant-account'), tenantId, balanceCents: 268_560_00n, creditCents: 50_000_00n, status: 'active' }, }); for (let index = 0; index < applicationIds.length; index += 1) { await prisma.smsApplication.upsert({ where: { cmppAccount: `SCREENSHOT_APP_${index + 1}` }, update: { tenantId, name: applicationNames[index], status: 'active', interfaceEnabled: true, customerUnitPrice: BigInt(index === 0 ? 8 : 10), }, create: { id: applicationIds[index], tenantId, name: applicationNames[index], scene: index === 0 ? '订单、验证码与服务通知' : '会员权益、节日活动与营销触达', cmppAccount: `SCREENSHOT_APP_${index + 1}`, cmppEnterpriseCode: `SG${String(index + 1).padStart(4, '0')}`, cmppApplicationExtension: String(21 + index), cmppClientSrcId: `1069008899${index + 1}`, secretHash: sha256(`screenshot-local-secret-${index + 1}`), interfaceEnabled: true, interfaceType: index === 0 ? 'cmpp30' : 'http', cmppMaxConnections: 3, cmppWindowSize: 32, dailyLimit: 500_000, customerUnitPrice: BigInt(index === 0 ? 8 : 10), queuePriority: index === 0 ? 'high' : 'normal', status: 'active', }, }); } const channelMeta = [ { code: 'SCREENSHOT-MOBILE', carrier: 'mobile', carriers: ['mobile'], price: 5 }, { code: 'SCREENSHOT-UNICOM', carrier: 'unicom', carriers: ['unicom'], price: 5 }, { code: 'SCREENSHOT-TELECOM', carrier: 'telecom', carriers: ['telecom'], price: 6 }, ]; for (let index = 0; index < channelIds.length; index += 1) { const meta = channelMeta[index]; await prisma.smsChannel.upsert({ where: { code: meta.code }, update: { name: channelNames[index], carrier: meta.carrier, carriers: meta.carriers, status: 'inactive', unitPrice: BigInt(meta.price) }, create: { id: channelIds[index], code: meta.code, name: channelNames[index], carrier: meta.carrier, carriers: meta.carriers, sendRegion: '全国', protocol: 'CMPP', gatewayHost: '127.0.0.1', gatewayPort: 17890 + index, enterpriseCode: `SGCH${index + 1}`, account: `screenshot_channel_${index + 1}`, passwordCipher: 'local-screenshot-seed-only', srcId: `10690088${index + 1}`, cmppVersion: '3.0', rateLimitPerSecond: 300 + index * 100, unitPrice: BigInt(meta.price), status: 'inactive', }, }); } for (let index = 0; index < signatureIds.length; index += 1) { await prisma.smsSignature.upsert({ where: { id: signatureIds[index] }, update: { name: signatureNames[index], auditStatus: 'approved', reportStatus: 'approved', pendingReport: false }, create: { id: signatureIds[index], tenantId, applicationId: applicationIds[index % 2], name: signatureNames[index], purpose: index === 1 ? '会员营销通知' : '交易与服务通知', auditStatus: 'approved', reportStatus: 'approved', pendingReport: false, reportChangedAt: dateDaysAgo(45), createdAt: dateDaysAgo(60), }, }); } const drainageMeta = [ { siteName: '星河优选商城', url: 'https://mall.example.test/benefits', remark: '会员积分兑换与新品活动页' }, { siteName: '星河夏日活动', url: 'https://events.example.test/summer', remark: '夏日专属优惠活动落地页' }, ]; for (let index = 0; index < drainageIds.length; index += 1) { await prisma.smsDrainageInfo.upsert({ where: { id: drainageIds[index] }, update: { ...drainageMeta[index], auditStatus: 'approved', pendingReport: false }, create: { id: drainageIds[index], tenantId, signatureId: signatureIds[1], applicationId: applicationIds[1], ...drainageMeta[index], reportValues: { ICP备案号: '浙ICP备20260088号', 业务类型: '会员权益活动' }, auditStatus: 'approved', pendingReport: false, reviewedAt: dateDaysAgo(35), submittedAt: dateDaysAgo(40), }, }); } } async function seedSendTasks() { const statuses = ['approved', 'pending_review', 'approved', 'rejected', 'approved', 'pending_review']; const decisions = ['allow', 'manual_review', 'allow', 'reject', 'allow', 'manual_review']; const contents = [ '【星河服务】您的订单已发货,物流单号已更新,请注意查收。', '【星河会员】您的会员积分将于本月底到期,可登录官网查看权益。', '【星河云】验证码 726418,5 分钟内有效,请勿告知他人。', '【星河会员】夏日优选活动已开启,会员可享限时积分兑换权益。', ]; const rows = Array.from({ length: 24 }, (_, index) => { const status = statuses[index % statuses.length]; return { id: id(`send-task-${String(index + 1).padStart(3, '0')}`), tenantId, applicationId: applicationIds[index % 2], taskNo: `ST-SHOT-${dateKeyDaysAgo(index % 8).replaceAll('-', '')}-${String(index + 1).padStart(4, '0')}`, sourceType: index % 3 === 0 ? 'client' : 'risk', content: contents[index % contents.length], category: index % 4 === 2 ? '验证码' : index % 2 === 0 ? '行业通知' : '会员营销', phoneTotal: 180 + index * 37, uniquePhoneTotal: 176 + index * 35, duplicateRatio: Number(((index % 5) * 0.012).toFixed(3)), illegalRatio: index % 7 === 0 ? 0.006 : 0, blacklistHitRatio: index % 6 === 0 ? 0.009 : 0, variableIssues: index % 5 === 0 ? { missing: 2, extra: 0 } : null, status, riskDecision: decisions[index % decisions.length], reviewReason: status === 'pending_review' ? '命中大批量营销内容人工复核阈值' : null, rejectReason: status === 'rejected' ? '营销内容缺少有效退订说明' : null, reviewedAt: status === 'approved' || status === 'rejected' ? dateDaysAgo(index % 8, 11, index) : null, createdAt: dateDaysAgo(index % 8, 9, index), updatedAt: dateDaysAgo(index % 8, 11, index), }; }); await prisma.smsSendTask.createMany({ data: rows }); return rows; } function messageStatus(index, batchIndex) { if (batchIndex === 10) return index < 7 ? 'submitted' : index < 12 ? 'queued' : 'scheduled'; if (batchIndex === 11) return index < 5 ? 'submit_failed' : index < 11 ? 'failed' : 'rejected'; const slot = (index * 7 + batchIndex * 3) % 20; if (slot < 14) return 'delivered'; if (slot < 16) return 'submitted'; if (slot < 18) return 'failed'; if (slot === 18) return 'timeout'; return 'rejected'; } async function seedBatchesAndMessages(sendTasks) { const provinces = ['浙江', '江苏', '广东', '北京', '上海', '四川', '湖北', '山东']; const carriers = ['mobile', 'unicom', 'telecom']; const contents = [ '【星河服务】您的订单 XH20260822001 已完成支付,感谢您的使用。', '【星河服务】您的快递已到达服务站,请凭取件码 8216 及时领取。', '【星河云】登录验证码 726418,5 分钟内有效。', '【星河会员】本周会员日权益已到账,点击活动页可查看详情。', '【星河会员】夏日优选活动进行中,会员专享积分兑换已开启。', ]; const batchRows = []; const messageRows = []; for (let batchIndex = 0; batchIndex < 12; batchIndex += 1) { const createdAt = dateDaysAgo(batchIndex % 7, 8 + (batchIndex % 9), batchIndex * 3); const statuses = Array.from({ length: 20 }, (_, index) => messageStatus(index, batchIndex)); const submittedTotal = statuses.filter((value) => !['queued', 'scheduled', 'rejected'].includes(value)).length; const successTotal = statuses.filter((value) => value === 'delivered').length; const failedTotal = statuses.filter((value) => ['failed', 'submit_failed', 'rejected'].includes(value)).length; const timeoutTotal = statuses.filter((value) => value === 'timeout').length; const unknownTotal = statuses.filter((value) => value === 'submitted').length; const batchStatus = batchIndex < 8 ? 'completed' : batchIndex < 10 ? 'sending' : batchIndex === 10 ? 'scheduled' : 'failed'; const batchId = id(`batch-${String(batchIndex + 1).padStart(3, '0')}`); batchRows.push({ id: batchId, tenantId, applicationId: applicationIds[batchIndex % 2], taskNo: `BT-SHOT-${dateKeyDaysAgo(batchIndex % 7).replaceAll('-', '')}-${String(batchIndex + 1).padStart(4, '0')}`, sourceType: batchIndex % 3 === 0 ? 'http' : 'client', content: contents[batchIndex % contents.length], category: batchIndex % 2 === 0 ? '行业通知' : '营销通知', phoneTotal: 20, status: batchStatus, auditStatus: batchIndex === 11 ? 'rejected' : 'approved', progressTotal: 20, submittedTotal, successTotal, failedTotal, unknownTotal, timeoutTotal, scheduledAt: batchIndex === 10 ? new Date(now.getTime() + 2 * 60 * 60 * 1000) : null, rejectReason: batchIndex === 11 ? '模板变量与受众字段不匹配' : null, createdAt, updatedAt: new Date(createdAt.getTime() + 18 * 60 * 1000), }); for (let index = 0; index < 20; index += 1) { const status = statuses[index]; const carrier = carriers[(index + batchIndex) % carriers.length]; const queuedAt = new Date(createdAt.getTime() + index * 41_000); const submittedAt = ['queued', 'scheduled', 'rejected'].includes(status) ? null : new Date(queuedAt.getTime() + 900 + (index % 5) * 240); const deliveredAt = status === 'delivered' ? new Date(submittedAt.getTime() + 1800 + (index % 9) * 720) : null; const units = index % 6 === 0 ? 2 : 1; const hasDrainage = batchIndex % 2 === 1 && index % 4 === 0; const messageContent = hasDrainage ? `${contents[batchIndex % contents.length]} 活动地址 https://events.example.test/summer` : contents[batchIndex % contents.length]; messageRows.push({ id: id(`message-${String(batchIndex + 1).padStart(3, '0')}-${String(index + 1).padStart(3, '0')}`), tenantId, batchTaskId: batchId, applicationId: applicationIds[batchIndex % 2], signatureId: signatureIds[batchIndex % signatureIds.length], drainageInfoId: hasDrainage ? drainageIds[(batchIndex + index) % 2] : null, reviewTaskId: sendTasks[(batchIndex * 2 + index) % sendTasks.length].id, messageId: `MSG-SHOT-${String(batchIndex + 1).padStart(3, '0')}-${String(index + 1).padStart(4, '0')}`, clientMessageId: `CLIENT-SHOT-${String(batchIndex + 1).padStart(3, '0')}-${String(index + 1).padStart(4, '0')}`, phoneNumber: `1380013${String(8000 + batchIndex * 20 + index).slice(-4)}`, carrier, province: provinces[(index + batchIndex * 2) % provinces.length], content: messageContent, hasDrainageContent: hasDrainage, drainageDetection: hasDrainage ? { matches: [{ start: messageContent.indexOf('https://'), end: messageContent.length, type: 'url' }], version: 'screenshot-v1' } : { matches: [], version: 'screenshot-v1' }, drainageDetectionVersion: 'screenshot-v1', drainageEvaluatedAt: queuedAt, billingUnits: units, unitPrice: BigInt(batchIndex % 2 === 0 ? 8 : 10), amountCents: BigInt(units * (batchIndex % 2 === 0 ? 8 : 10)), queuePriority: batchIndex % 3 === 0 ? 'high' : 'normal', channelId: channelIds[(index + batchIndex) % channelIds.length], submitId: submittedAt ? `SUBMIT-SHOT-${batchIndex + 1}-${index + 1}` : null, gatewayMessageId: submittedAt ? String(8_600_000_000_000 + batchIndex * 100 + index) : null, status, submitStatus: status === 'rejected' ? 'rejected' : submittedAt ? 'accepted' : null, receiptStatus: status === 'delivered' ? 'delivered' : status === 'failed' ? 'undelivered' : null, receiptRawStatus: status === 'delivered' ? 'DELIVRD' : status === 'failed' ? 'UNDELIV' : null, errorCode: status === 'submit_failed' ? 'CMPP-8' : status === 'failed' ? 'YX:0003' : status === 'timeout' ? 'RECEIPT_TIMEOUT' : status === 'rejected' ? 'RISK_REJECTED' : null, errorMessage: status === 'submit_failed' ? '供应商通道暂时拒绝' : status === 'failed' ? '号码空号或停机' : status === 'timeout' ? '72 小时未收到最终回执' : status === 'rejected' ? '命中营销风控规则' : null, queuedAt, submittedAt, deliveredAt, timeoutAt: status === 'timeout' ? new Date(queuedAt.getTime() + 72 * 60 * 60 * 1000) : null, updatedAt: deliveredAt ?? submittedAt ?? queuedAt, }); } } await prisma.smsBatchTask.createMany({ data: batchRows }); await prisma.smsMessageRecord.createMany({ data: messageRows }); return { batchRows, messageRows }; } async function seedProtocolLogs(messages) { const eventTypes = ['submit', 'submit_resp', 'deliver_receipt', 'deliver_resp', 'send_request', 'receipt_webhook', 'connect']; const directions = ['client_to_platform', 'platform_to_channel', 'channel_to_platform', 'platform_to_client']; const statuses = ['success', 'accepted', 'success', 'received', 'success', 'retrying', 'failed']; const rows = Array.from({ length: 180 }, (_, index) => { const message = messages[index % messages.length]; const eventType = eventTypes[index % eventTypes.length]; const protocol = index % 5 === 0 || index % 5 === 4 ? 'http' : 'cmpp'; const direction = directions[index % directions.length]; const status = statuses[index % statuses.length]; const daysAgo = index < 112 ? 0 : 1 + (index % 6); const createdAt = dateDaysAgo(daysAgo, 8 + (index % 12), (index * 7) % 60); return { id: id(`protocol-${String(index + 1).padStart(4, '0')}`), protocol, direction, eventType, status, tenantId, applicationId: message.applicationId, channelId: message.channelId, account: protocol === 'http' ? `AK-SHOT-${(index % 2) + 1}` : `SCREENSHOT_APP_${(index % 2) + 1}`, messageId: message.messageId, gatewayMessageId: message.gatewayMessageId, traceId: `TRACE-SHOT-${String(index + 1).padStart(6, '0')}`, requestId: protocol === 'http' ? `REQ-SHOT-${String(index + 1).padStart(6, '0')}` : null, phoneNumber: message.phoneNumber, resultCode: status === 'failed' ? (protocol === 'http' ? '429' : '8') : status === 'retrying' ? 'RETRY-1' : '0', durationMs: 18 + ((index * 37) % 680), payloadBytes: 96 + ((index * 53) % 1200), retryCount: status === 'retrying' ? 1 + (index % 3) : 0, detail: { gateway: `local-gateway-${(index % 2) + 1}`, windowSize: 32, segmentCount: index % 9 === 0 ? 2 : 1, result: status }, createdAt, }; }); await prisma.protocolInteractionLog.createMany({ data: rows }); return rows; } async function seedReports() { const reconciliation = []; const profit = []; const quality = []; for (let days = 1; days <= 30; days += 1) { const date = reportDate(days); for (let appIndex = 0; appIndex < applicationIds.length; appIndex += 1) { const submitted = 8200 + ((days * 977 + appIndex * 2231) % 12_000); const failed = 70 + ((days * 41 + appIndex * 67) % 260); const unknown = 18 + ((days * 17 + appIndex * 13) % 95); const success = submitted - failed - unknown; const sent = success + failed + unknown; reconciliation.push({ id: id(`recon-${days}-${appIndex}`), reportDate: date, tenantId, tenantName, applicationId: applicationIds[appIndex], applicationName: applicationNames[appIndex], submittedUnits: submitted, sentUnits: sent, unknownUnits: unknown, successUnits: success, failedUnits: failed, generatedAt: dateDaysAgo(days - 1, 2, 12), updatedAt: dateDaysAgo(days - 1, 2, 12), }); const revenue = BigInt(success * (appIndex === 0 ? 8 : 10)); const cost = BigInt(success * (appIndex === 0 ? 5 : 6)); const appProfit = revenue - cost; profit.push({ id: id(`profit-app-${days}-${appIndex}`), reportDate: date, dimensionType: 'application', dimensionId: applicationIds[appIndex], dimensionName: applicationNames[appIndex], tenantId, tenantName, applicationId: applicationIds[appIndex], submittedUnits: submitted, sentUnits: sent, unknownUnits: unknown, successUnits: success, failedUnits: failed, revenueCents: revenue, refundCents: 0n, costCents: cost, profitCents: appProfit, profitRateBps: Number(appProfit * 10_000n / revenue), generatedAt: dateDaysAgo(days - 1, 2, 18), updatedAt: dateDaysAgo(days - 1, 2, 18), }); quality.push({ id: id(`quality-app-${days}-${appIndex}`), reportDate: date, dimensionType: 'application', dimensionId: applicationIds[appIndex], dimensionName: applicationNames[appIndex], tenantId, tenantName, applicationId: applicationIds[appIndex], submittedUnits: submitted, sentUnits: sent, unknownUnits: unknown, successUnits: success, failedUnits: failed, successRateBps: Math.round(success * 10_000 / sent), avgArrivalMs: 1800 + ((days * 173 + appIndex * 641) % 5200), generatedAt: dateDaysAgo(days - 1, 2, 25), updatedAt: dateDaysAgo(days - 1, 2, 25), }); } for (let channelIndex = 0; channelIndex < channelIds.length; channelIndex += 1) { const submitted = 4600 + ((days * 587 + channelIndex * 1871) % 8500); const failed = 35 + ((days * 29 + channelIndex * 43) % 180); const unknown = 12 + ((days * 11 + channelIndex * 7) % 72); const success = submitted - failed - unknown; const sent = submitted; const revenue = BigInt(success * 9); const cost = BigInt(success * (channelIndex === 2 ? 6 : 5)); const channelProfit = revenue - cost; profit.push({ id: id(`profit-channel-${days}-${channelIndex}`), reportDate: date, dimensionType: 'channel', dimensionId: channelIds[channelIndex], dimensionName: channelNames[channelIndex], channelId: channelIds[channelIndex], submittedUnits: submitted, sentUnits: sent, unknownUnits: unknown, successUnits: success, failedUnits: failed, revenueCents: revenue, refundCents: 0n, costCents: cost, profitCents: channelProfit, profitRateBps: Number(channelProfit * 10_000n / revenue), generatedAt: dateDaysAgo(days - 1, 2, 20), updatedAt: dateDaysAgo(days - 1, 2, 20), }); quality.push({ id: id(`quality-channel-${days}-${channelIndex}`), reportDate: date, dimensionType: 'channel', dimensionId: channelIds[channelIndex], dimensionName: channelNames[channelIndex], channelId: channelIds[channelIndex], submittedUnits: submitted, sentUnits: sent, unknownUnits: unknown, successUnits: success, failedUnits: failed, successRateBps: Math.round(success * 10_000 / sent), avgArrivalMs: 1450 + ((days * 137 + channelIndex * 991) % 4800), generatedAt: dateDaysAgo(days - 1, 2, 27), updatedAt: dateDaysAgo(days - 1, 2, 27), }); } for (let signatureIndex = 0; signatureIndex < signatureIds.length; signatureIndex += 1) { const sent = 3300 + ((days * 431 + signatureIndex * 1201) % 6200); const failed = 28 + ((days * 23 + signatureIndex * 31) % 130); const unknown = 8 + ((days * 7 + signatureIndex * 5) % 45); const success = sent - failed - unknown; quality.push({ id: id(`quality-signature-${days}-${signatureIndex}`), reportDate: date, dimensionType: 'signature', dimensionId: signatureIds[signatureIndex], dimensionName: signatureNames[signatureIndex], tenantId, tenantName, applicationId: applicationIds[signatureIndex % 2], signatureId: signatureIds[signatureIndex], submittedUnits: sent, sentUnits: sent, unknownUnits: unknown, successUnits: success, failedUnits: failed, successRateBps: Math.round(success * 10_000 / sent), avgArrivalMs: 1900 + ((days * 149 + signatureIndex * 733) % 5100), generatedAt: dateDaysAgo(days - 1, 2, 29), updatedAt: dateDaysAgo(days - 1, 2, 29), }); } for (let drainageIndex = 0; drainageIndex < drainageIds.length; drainageIndex += 1) { const sent = 1250 + ((days * 277 + drainageIndex * 881) % 3600); const failed = 18 + ((days * 13 + drainageIndex * 19) % 85); const unknown = 5 + ((days * 5 + drainageIndex * 3) % 28); const success = sent - failed - unknown; quality.push({ id: id(`quality-drainage-${days}-${drainageIndex}`), reportDate: date, dimensionType: 'drainage', dimensionId: drainageIds[drainageIndex], dimensionName: drainageIndex === 0 ? '星河优选商城' : '星河夏日活动', tenantId, tenantName, applicationId: applicationIds[1], signatureId: signatureIds[1], drainageInfoId: drainageIds[drainageIndex], submittedUnits: sent, sentUnits: sent, unknownUnits: unknown, successUnits: success, failedUnits: failed, successRateBps: Math.round(success * 10_000 / sent), avgArrivalMs: 2300 + ((days * 181 + drainageIndex * 521) % 5800), generatedAt: dateDaysAgo(days - 1, 2, 31), updatedAt: dateDaysAgo(days - 1, 2, 31), }); } } await prisma.dailyReconciliationReport.createMany({ data: reconciliation, skipDuplicates: true }); await prisma.dailyProfitReport.createMany({ data: profit, skipDuplicates: true }); await prisma.dailyQualityReport.createMany({ data: quality, skipDuplicates: true }); return { reconciliation, profit, quality }; } async function main() { await clearPreviousSeed(); await ensureDimensions(); const sendTasks = await seedSendTasks(); const { batchRows, messageRows } = await seedBatchesAndMessages(sendTasks); const protocolRows = await seedProtocolLogs(messageRows); const reports = await seedReports(); console.log(JSON.stringify({ seed: PREFIX, tenant: tenantName, applications: applicationNames.length, channels: channelNames.length, sendTasks: sendTasks.length, batchTasks: batchRows.length, messages: messageRows.length, protocolLogs: protocolRows.length, reconciliationReports: reports.reconciliation.length, profitReports: reports.profit.length, qualityReports: reports.quality.length, }, null, 2)); } main() .finally(async () => prisma.$disconnect()) .catch((error) => { console.error(error); process.exitCode = 1; });