import { PrismaPg } from '../../api/node_modules/@prisma/adapter-pg/dist/index.js'; import { PrismaClient } from '../../api/node_modules/@prisma/client/index.js'; const prisma = new PrismaClient({ adapter: new PrismaPg(process.env.DATABASE_URL ?? 'postgresql://cmpp:cmpp_password@localhost:5432/cmpp_platform?schema=public'), }); const signatureIds = ['qa-retirement-signature-steady', 'qa-retirement-signature-low', 'qa-retirement-signature-zero', 'qa-retirement-signature-retry', 'qa-retirement-signature-unreported', 'qa-retirement-signature-partial']; const taskIds = ['qa-retirement-task-steady-mobile', 'qa-retirement-task-steady-unicom', 'qa-retirement-task-low-unicom', 'qa-retirement-task-zero-telecom', 'qa-retirement-task-retry-primary', 'qa-retirement-task-retry-backup', 'qa-retirement-task-partial-mobile']; const todayKey = new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit' }).format(new Date()); const todayNoon = new Date(`${todayKey}T12:00:00+08:00`); const approvedAt = new Date(todayNoon.getTime() - 100 * 86_400_000); try { await cleanup(); const tenantA = await prisma.tenant.upsert({ where: { code: 'qa-retirement-a' }, update: { name: '清退验收企业A', status: 'active', certificationStatus: 'approved' }, create: { id: 'qa-retirement-tenant-a', code: 'qa-retirement-a', name: '清退验收企业A', status: 'active', certificationStatus: 'approved' }, }); const tenantB = await prisma.tenant.upsert({ where: { code: 'qa-retirement-b' }, update: { name: '清退验收企业B', status: 'active', certificationStatus: 'approved' }, create: { id: 'qa-retirement-tenant-b', code: 'qa-retirement-b', name: '清退验收企业B', status: 'active', certificationStatus: 'approved' }, }); const appA = await prisma.smsApplication.upsert({ where: { cmppAccount: 'qa_retirement_a' }, update: { tenantId: tenantA.id, name: '清退验收通知应用', status: 'active' }, create: { id: 'qa-retirement-app-a', tenantId: tenantA.id, name: '清退验收通知应用', cmppAccount: 'qa_retirement_a', cmppEnterpriseCode: 'QA_RET_A', secretHash: 'local-seed-only', status: 'active' }, }); const appB = await prisma.smsApplication.upsert({ where: { cmppAccount: 'qa_retirement_b' }, update: { tenantId: tenantB.id, name: '清退验收营销应用', status: 'active' }, create: { id: 'qa-retirement-app-b', tenantId: tenantB.id, name: '清退验收营销应用', cmppAccount: 'qa_retirement_b', cmppEnterpriseCode: 'QA_RET_B', secretHash: 'local-seed-only', status: 'active' }, }); const channelAll = await upsertChannel('qa-retirement-all', '验收通道-华东三网', ['mobile', 'unicom', 'telecom'], 42000n); const channelMu = await upsertChannel('qa-retirement-mu', '验收通道-移动联通', ['mobile', 'unicom'], 39000n); const channelTelecom = await upsertChannel('qa-retirement-telecom', '验收通道-电信专线', ['telecom'], 41000n); const steady = await upsertSignature(signatureIds[0], tenantA.id, appA.id, '【验收稳定活跃】'); const low = await upsertSignature(signatureIds[1], tenantA.id, appA.id, '【验收低量预警】'); const zero = await upsertSignature(signatureIds[2], tenantB.id, appB.id, '【验收零量预警】'); const retry = await upsertSignature(signatureIds[3], tenantB.id, appB.id, '【验收跨通道补发】'); const unreported = await upsertSignature(signatureIds[4], tenantA.id, appA.id, '【验收完全未报备】', 'waiting_material'); const partial = await upsertSignature(signatureIds[5], tenantB.id, appB.id, '【验收仅移动已报备】'); await createTask(taskIds[0], tenantA.id, steady.id, channelAll.id, 'mobile'); await createTask(taskIds[1], tenantA.id, steady.id, channelAll.id, 'unicom'); await createTask(taskIds[2], tenantA.id, low.id, channelMu.id, 'unicom'); await createTask(taskIds[3], tenantB.id, zero.id, channelTelecom.id, 'telecom'); await createTask(taskIds[4], tenantB.id, retry.id, channelAll.id, 'mobile'); await createTask(taskIds[5], tenantB.id, retry.id, channelMu.id, 'mobile'); await createTask(taskIds[6], tenantB.id, partial.id, channelAll.id, 'mobile'); await prisma.signatureRetirementRule.upsert({ where: { ruleType_targetKey: { ruleType: 'enterprise_global', targetKey: '' } }, update: { enabled: true, mobileWindowDays: 30, mobileThreshold: 15, unicomWindowDays: 30, unicomThreshold: 15, telecomWindowDays: 30, telecomThreshold: 15, version: { increment: 1 } }, create: { ruleType: 'enterprise_global', targetKey: '', enabled: true, mobileWindowDays: 30, mobileThreshold: 15, unicomWindowDays: 30, unicomThreshold: 15, telecomWindowDays: 30, telecomThreshold: 15 }, }); await prisma.signatureRetirementRule.upsert({ where: { ruleType_targetKey: { ruleType: 'channel_global', targetKey: '' } }, update: { enabled: true, mobileWindowDays: 30, mobileThreshold: 15, unicomWindowDays: 30, unicomThreshold: 15, telecomWindowDays: 30, telecomThreshold: 15, version: { increment: 1 } }, create: { ruleType: 'channel_global', targetKey: '', enabled: true, mobileWindowDays: 30, mobileThreshold: 15, unicomWindowDays: 30, unicomThreshold: 15, telecomWindowDays: 30, telecomThreshold: 15 }, }); for (let day = 1; day <= 30; day += 1) { await addAcceptedMessage({ key: `steady-${day}`, tenantId: tenantA.id, applicationId: appA.id, signatureId: steady.id, carrier: 'mobile', day, channelId: channelAll.id, delivered: day % 7 !== 0 }); } for (const day of [3, 10, 20, 29]) { await addAcceptedMessage({ key: `low-${day}`, tenantId: tenantA.id, applicationId: appA.id, signatureId: low.id, carrier: 'unicom', day, channelId: channelMu.id, delivered: day !== 20 }); } for (const day of [2, 6, 10, 14, 18, 22, 26, 29]) { await addRetryMessage({ key: `retry-${day}`, tenantId: tenantB.id, applicationId: appB.id, signatureId: retry.id, day, failedChannelId: channelAll.id, acceptedChannelId: channelMu.id }); } for (let index = 1; index <= 6; index += 1) { await addAcceptedMessage({ key: `unreported-${index}`, tenantId: tenantA.id, applicationId: appA.id, signatureId: unreported.id, carrier: 'mobile', day: 0, channelId: channelAll.id, delivered: index <= 5 }); } for (let index = 1; index <= 4; index += 1) { await addAcceptedMessage({ key: `partial-telecom-${index}`, tenantId: tenantB.id, applicationId: appB.id, signatureId: partial.id, carrier: 'telecom', day: 0, channelId: channelTelecom.id, delivered: true }); } console.log(JSON.stringify({ todayKey, tenants: 2, applications: 2, channels: 3, signatures: 6, carrierReportTasks: 7, messageRecords: 52, scenarios: ['稳定活跃', '低量预警', '零量预警', '跨通道补发', '完全未报备', '仅其他运营商已报备'], })); } finally { await prisma.$disconnect(); } async function cleanup() { const detections = await prisma.signatureRetirementDetection.findMany({ where: { signatureId: { in: signatureIds } }, select: { id: true } }); const detectionIds = detections.map((item) => item.id); await prisma.signatureRetirementMessage.deleteMany({ where: { detectionId: { in: detectionIds } } }); await prisma.signatureRetirementSuppression.deleteMany({ where: { signatureId: { in: signatureIds } } }); await prisma.signatureRetirementDetection.deleteMany({ where: { id: { in: detectionIds } } }); await prisma.signatureRetirementCycle.deleteMany({ where: { signatureId: { in: signatureIds } } }); const messages = await prisma.smsMessageRecord.findMany({ where: { messageId: { startsWith: 'QA-RETIREMENT-' } }, select: { id: true } }); const messageIds = messages.map((item) => item.id); await prisma.smsReceiptRecord.deleteMany({ where: { messageRecordId: { in: messageIds } } }); await prisma.smsMessageSegmentAudit.deleteMany({ where: { messageRecordId: { in: messageIds } } }); await prisma.smsSubmitRecord.deleteMany({ where: { messageRecordId: { in: messageIds } } }); await prisma.smsMessageRecord.deleteMany({ where: { id: { in: messageIds } } }); await prisma.channelSignatureReportRecord.deleteMany({ where: { taskId: { in: taskIds } } }); await prisma.channelSignatureReportTask.deleteMany({ where: { id: { in: taskIds } } }); } async function upsertChannel(code, name, carriers, unitPrice) { return prisma.smsChannel.upsert({ where: { code }, update: { name, carrier: carriers.length === 3 ? 'all' : carriers.length === 1 ? carriers[0] : 'multi', carriers, unitPrice, status: 'disabled' }, create: { code, name, carrier: carriers.length === 3 ? 'all' : carriers.length === 1 ? carriers[0] : 'multi', carriers, sendRegion: '全国', protocol: 'CMPP', gatewayHost: '127.0.0.1', gatewayPort: 17890, enterpriseCode: 'QA_LOCAL', account: code, passwordCipher: 'local-seed-only', srcId: '10690000', cmppVersion: '3.0', rateLimitPerSecond: 100, unitPrice, status: 'disabled' }, }); } async function upsertSignature(id, tenantId, applicationId, name, reportStatus = 'approved') { return prisma.smsSignature.upsert({ where: { id }, update: { tenantId, applicationId, name, auditStatus: 'approved', reportStatus, pendingReport: reportStatus !== 'approved' }, create: { id, tenantId, applicationId, name, purpose: '本地签名清退验收', auditStatus: 'approved', reportStatus, pendingReport: reportStatus !== 'approved' }, }); } async function createTask(id, tenantId, signatureId, channelId, carrier) { await prisma.channelSignatureReportTask.create({ data: { id, tenantId, signatureId, channelId, carrier, approvedAt, approvalScope: 'carrier_specific', reportType: 'signature', status: 'approved', reason: '本地签名清退验收数据' } }); await prisma.channelSignatureReportRecord.create({ data: { taskId: id, channelId, action: 'local_qa_approved', statusBefore: 'reporting', statusAfter: 'approved', reason: '本地签名清退验收数据', sourceEntry: 'system' } }); } async function addAcceptedMessage({ key, tenantId, applicationId, signatureId, carrier, day, channelId, delivered }) { const at = new Date(todayNoon.getTime() - day * 86_400_000); const record = await prisma.smsMessageRecord.create({ data: { id: `qa-retirement-message-${key}`, tenantId, applicationId, signatureId, messageId: `QA-RETIREMENT-${key.toUpperCase()}`, phoneNumber: carrier === 'mobile' ? '13800138000' : '18600186000', carrier, content: `【本地验收】${key}`, channelId, status: delivered ? 'delivered' : 'submitted', submitStatus: 'accepted', receiptStatus: delivered ? 'delivered' : 'failed', queuedAt: at, submittedAt: at, deliveredAt: delivered ? at : null } }); const submit = await prisma.smsSubmitRecord.create({ data: { id: `qa-retirement-submit-${key}`, tenantId, messageRecordId: record.id, channelId, submitId: `QA-SUBMIT-${key.toUpperCase()}`, gatewayMessageId: `QA-GW-${key.toUpperCase()}`, submitStatus: 'accepted', submittedAt: at, createdAt: at } }); await prisma.smsMessageSegmentAudit.create({ data: { id: `qa-retirement-segment-${key}`, tenantId, messageRecordId: record.id, submitRecordId: submit.id, channelId, submitId: submit.submitId, gatewayMessageId: submit.gatewayMessageId, submitStatus: 'accepted', receiptStatus: delivered ? 'delivered' : 'failed', rawStatus: delivered ? 'DELIVRD' : 'UNDELIV', submittedAt: at, deliveredAt: delivered ? at : null, createdAt: at } }); } async function addRetryMessage({ key, tenantId, applicationId, signatureId, day, failedChannelId, acceptedChannelId }) { const at = new Date(todayNoon.getTime() - day * 86_400_000); const record = await prisma.smsMessageRecord.create({ data: { id: `qa-retirement-message-${key}`, tenantId, applicationId, signatureId, messageId: `QA-RETIREMENT-${key.toUpperCase()}`, phoneNumber: '13800138001', carrier: 'mobile', content: `【本地验收】${key}`, channelId: acceptedChannelId, status: 'delivered', submitStatus: 'accepted', receiptStatus: 'delivered', queuedAt: at, submittedAt: at, deliveredAt: at } }); const failed = await prisma.smsSubmitRecord.create({ data: { id: `qa-retirement-submit-${key}-failed`, tenantId, messageRecordId: record.id, channelId: failedChannelId, submitId: `QA-SUBMIT-${key.toUpperCase()}-FAILED`, submitStatus: 'failed', errorCode: 'LOCAL_TIMEOUT', errorMessage: '本地模拟通道超时,仅用于历史统计', submittedAt: at, createdAt: at } }); await prisma.smsMessageSegmentAudit.create({ data: { id: `qa-retirement-segment-${key}-failed`, tenantId, messageRecordId: record.id, submitRecordId: failed.id, channelId: failedChannelId, submitId: failed.submitId, submitStatus: 'failed', errorCode: 'LOCAL_TIMEOUT', submittedAt: at, createdAt: at } }); const accepted = await prisma.smsSubmitRecord.create({ data: { id: `qa-retirement-submit-${key}-accepted`, tenantId, messageRecordId: record.id, channelId: acceptedChannelId, retryOfSubmitRecordId: failed.id, submitId: `QA-SUBMIT-${key.toUpperCase()}-ACCEPTED`, gatewayMessageId: `QA-GW-${key.toUpperCase()}`, submitStatus: 'accepted', submittedAt: new Date(at.getTime() + 60_000), createdAt: new Date(at.getTime() + 60_000) } }); await prisma.smsMessageSegmentAudit.create({ data: { id: `qa-retirement-segment-${key}-accepted`, tenantId, messageRecordId: record.id, submitRecordId: accepted.id, channelId: acceptedChannelId, submitId: accepted.submitId, gatewayMessageId: accepted.gatewayMessageId, submitStatus: 'accepted', receiptStatus: 'delivered', rawStatus: 'DELIVRD', submittedAt: accepted.submittedAt, deliveredAt: new Date(at.getTime() + 120_000), createdAt: accepted.createdAt } }); }