Files
lislgosms/api/prisma/schema.prisma
T

2324 lines
77 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
}
model Tenant {
id String @id @default(cuid())
name String
code String @unique
status String @default("active")
certificationStatus String @default("approved")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
users User[]
enterpriseCertifications EnterpriseCertification[]
operationLogs OperationLog[]
fileObjects FileObject[]
enterpriseBlacklists EnterpriseBlacklist[]
accounts TenantAccount[]
accountTransactions AccountTransaction[]
rechargeOrders RechargeOrder[]
smsBillingRecords SmsBillingRecord[]
smsApplications SmsApplication[]
smsSignatures SmsSignature[]
smsDrainageInfos SmsDrainageInfo[]
smsTemplates SmsTemplate[]
auditRecords AuditRecord[]
riskRules RiskRule[]
smsSendTasks SmsSendTask[]
riskHitRecords RiskHitRecord[]
phoneFrequencyStates PhoneFrequencyState[]
phoneFrequencyHits PhoneFrequencyHit[]
smsBatchTasks SmsBatchTask[]
smsMessageRecords SmsMessageRecord[]
smsApiRequests SmsApiRequest[]
smsSubmitRecords SmsSubmitRecord[]
smsReceiptRecords SmsReceiptRecord[]
smsMessageSegmentAudits SmsMessageSegmentAudit[]
smsUplinkMessages SmsUplinkMessage[]
smsUplinkMatchCandidates SmsUplinkMatchCandidate[]
cmppDownstreamDeliveries CmppDownstreamDelivery[]
downstreamRequeueTasks DownstreamRequeueTask[]
cmppDownstreamConnections CmppDownstreamConnection[]
cmppConnectionStates CmppConnectionState[]
gatewayDownstreamRecoveryStatuses GatewayDownstreamRecoveryStatus[]
gatewaySubmitDeadLetters GatewaySubmitDeadLetter[]
smsReceiptAnomalies SmsReceiptAnomaly[]
openApiRequests OpenApiRequest[]
httpWebhookEvents HttpWebhookEvent[]
cmppInboundLongMessages CmppInboundLongMessage[]
}
model EnterpriseCertification {
id String @id @default(cuid())
tenantId String
companyName String
licenseNo String?
contactName String?
contactPhone String?
materials Json?
status String @default("pending")
rejectReason String?
reviewerId String?
submittedAt DateTime @default(now())
reviewedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
@@index([tenantId, status, createdAt])
}
model User {
id String @id @default(cuid())
tenantId String?
username String
email String?
phone String?
displayName String
passwordHash String
status String @default("active")
sessionVersion Int @default(0)
failedLoginCount Int @default(0)
lockedUntil DateTime?
lastLoginAt DateTime?
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant? @relation(fields: [tenantId], references: [id])
roles UserRole[]
operationLogs OperationLog[]
auditRecords AuditRecord[]
createdSmsSendTasks SmsSendTask[] @relation("SmsSendTaskCreator")
reviewedSmsSendTasks SmsSendTask[] @relation("SmsSendTaskReviewer")
createdSmsBatchTasks SmsBatchTask[] @relation("SmsBatchTaskCreator")
createdDownstreamRequeueTasks DownstreamRequeueTask[] @relation("DownstreamRequeueTaskCreator")
releasedPhoneFrequencyHits PhoneFrequencyHit[] @relation("PhoneFrequencyHitReleaser")
createdPhoneFrequencyWhitelistEntries PhoneFrequencyWhitelist[] @relation("PhoneFrequencyWhitelistCreator")
updatedPhoneFrequencyWhitelistEntries PhoneFrequencyWhitelist[] @relation("PhoneFrequencyWhitelistUpdater")
}
model Role {
id String @id @default(cuid())
code String @unique
name String
scope String @default("platform")
description String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
users UserRole[]
permissions RolePermission[]
}
model Permission {
id String @id @default(cuid())
code String @unique
name String
description String?
createdAt DateTime @default(now())
roles RolePermission[]
}
model UserRole {
userId String
roleId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
@@id([userId, roleId])
}
model RolePermission {
roleId String
permissionId String
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)
@@id([roleId, permissionId])
}
model OperationLog {
id String @id @default(cuid())
tenantId String?
userId String?
action String
resource String
resourceId String?
ipAddress String?
userAgent String?
detail Json?
createdAt DateTime @default(now())
tenant Tenant? @relation(fields: [tenantId], references: [id])
user User? @relation(fields: [userId], references: [id])
@@index([tenantId, createdAt])
@@index([userId, createdAt])
@@index([createdAt])
@@index([resource, createdAt])
}
model OperationLogArchive {
originalId String @id
tenantId String?
userId String?
action String
resource String
resourceId String?
ipAddress String?
userAgent String?
detail Json?
createdAt DateTime
archiveMonth String
archivedAt DateTime @default(now())
@@index([tenantId, createdAt])
@@index([userId, createdAt])
@@index([createdAt])
@@index([resource, createdAt])
@@index([archiveMonth])
}
model ProtocolInteractionLog {
id String @id @default(cuid())
protocol String
direction String
eventType String
status String
tenantId String?
applicationId String?
channelId String?
account String?
messageId String?
gatewayMessageId String?
traceId String?
requestId String?
phoneMasked String?
phoneNumber String?
resultCode String?
durationMs Int?
payloadBytes Int?
retryCount Int?
detail Json?
createdAt DateTime @default(now())
@@index([createdAt])
@@index([protocol, direction, createdAt])
@@index([eventType, createdAt])
@@index([status, createdAt])
@@index([channelId, createdAt])
@@index([applicationId, createdAt])
@@index([messageId])
@@index([gatewayMessageId])
@@index([requestId])
}
model FileObject {
id String @id @default(cuid())
tenantId String?
bucket String
objectKey String @unique
fileName String
contentType String
sizeBytes BigInt
checksum String?
purpose String
createdAt DateTime @default(now())
tenant Tenant? @relation(fields: [tenantId], references: [id])
}
model PhoneSegment {
id String @id @default(cuid())
prefix String @unique
carrier String
province String?
city String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model PhoneCarrierRule {
id String @id @default(cuid())
carrier String
pattern String
priority Int @default(100)
status String @default("active")
remark String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status, priority])
}
model SensitiveWord {
id String @id @default(cuid())
word String @unique
level String @default("block")
status String @default("active")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model EnterpriseBlacklist {
id String @id @default(cuid())
tenantId String
applicationId String
phoneNumber String
reason String?
status String @default("active")
createdAt DateTime @default(now())
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication @relation(fields: [applicationId], references: [id])
@@unique([applicationId, phoneNumber])
}
model GlobalBlacklist {
id String @id @default(cuid())
phoneNumber String @unique
reason String?
status String @default("active")
createdAt DateTime @default(now())
}
model DrainageField {
id String @id @default(cuid())
code String @unique
name String
fieldType String
required Boolean @default(false)
status String @default("active")
description String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
channelReportFields ChannelReportField[]
commonReportFields CommonReportField[]
}
model DrainageDetectionRule {
id String @id @default(cuid())
code String @unique
name String
category String
pattern String
flags String @default("giu")
priority Int @default(100)
status String @default("active")
description String?
version Int @default(1)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status, priority])
}
model TenantAccount {
id String @id @default(cuid())
tenantId String
balanceCents BigInt @default(0)
creditCents BigInt @default(0)
status String @default("active")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
@@unique([tenantId])
}
model AccountTransaction {
id String @id @default(cuid())
tenantId String
transactionType String
idempotencyKey String? @unique
amountCents BigInt @default(0)
balanceAfter BigInt @default(0)
relatedType String?
relatedId String?
remark String?
createdAt DateTime @default(now())
tenant Tenant @relation(fields: [tenantId], references: [id])
@@index([tenantId, createdAt])
@@index([relatedType, relatedId])
}
model BillingRule {
id String @id @default(cuid())
code String @unique
name String
chargeBasis String @default("submit_success")
unitPrice BigInt
status String @default("active")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model RechargeOrder {
id String @id @default(cuid())
tenantId String
orderNo String @unique
amountCents BigInt
status String @default("created")
payMethod String?
paidAt DateTime?
operatorId String?
remark String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
@@index([tenantId, createdAt])
@@index([payMethod, createdAt])
}
model SmsBillingRecord {
id String @id @default(cuid())
tenantId String
applicationId String?
taskId String?
messageId String?
phoneNumber String?
contentLength Int
billingUnits Int
unitPrice BigInt
amountCents BigInt
billingStatus String @default("estimated")
transactionId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
@@index([tenantId, createdAt])
@@index([taskId])
@@index([messageId])
}
model SmsApplication {
id String @id @default(cuid())
tenantId String
name String
scene String?
callbackUrl String?
cmppAccount String @unique
cmppEnterpriseCode String
cmppApplicationExtension String?
cmppAccessNumberFillEnabled Boolean @default(false)
cmppAccessNumberFillPrefix String?
cmppClientSrcId String? @unique
secretHash String
interfaceEnabled Boolean @default(true)
interfaceType String @default("cmpp20")
cmppMaxConnections Int @default(1)
cmppWindowSize Int @default(16)
dailyLimit Int @default(100000)
customerUnitPrice BigInt @default(0)
queuePriority String @default("normal")
templateMismatchMode String @default("reject")
downstreamReceiptRetryEnabled Boolean @default(true)
downstreamUplinkRetryEnabled Boolean @default(true)
status String @default("active")
disablingAt DateTime?
autoDisableAt DateTime?
disableReason String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
ipAllowlist SmsApplicationIpAllowlist[]
signatures SmsSignature[]
drainageInfos SmsDrainageInfo[]
templates SmsTemplate[]
enterpriseBlacklists EnterpriseBlacklist[]
sendTasks SmsSendTask[]
batchTasks SmsBatchTask[]
messageRecords SmsMessageRecord[]
uplinkMessages SmsUplinkMessage[]
uplinkMatchCandidates SmsUplinkMatchCandidate[]
downstreamDeliveries CmppDownstreamDelivery[]
downstreamRequeueTasks DownstreamRequeueTask[]
downstreamConnections CmppDownstreamConnection[]
connectionStates CmppConnectionState[]
gatewayDownstreamRecoveryStatuses GatewayDownstreamRecoveryStatus[]
gatewaySubmitDeadLetters GatewaySubmitDeadLetter[]
receiptAnomalies SmsReceiptAnomaly[]
httpConfig SmsApplicationHttpConfig?
httpIpAllowlist SmsApplicationHttpIpAllowlist[]
httpApiCredentials HttpApiCredential[]
openApiRequests OpenApiRequest[]
httpWebhookEndpoints HttpWebhookEndpoint[]
httpWebhookEvents HttpWebhookEvent[]
dailyUsages SmsApplicationDailyUsage[]
inboundLongMessages CmppInboundLongMessage[]
riskRules RiskRule[]
phoneFrequencyStates PhoneFrequencyState[]
phoneFrequencyHits PhoneFrequencyHit[]
@@index([tenantId, status])
@@index([status, createdAt])
@@index([status, autoDisableAt])
}
model SmsApplicationIpAllowlist {
id String @id @default(cuid())
applicationId String
ipCidr String
remark String?
createdAt DateTime @default(now())
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
@@unique([applicationId, ipCidr])
}
model SmsApplicationHttpConfig {
id String @id @default(cuid())
applicationId String @unique
enabled Boolean @default(false)
sendEnabled Boolean @default(true)
messageQueryEnabled Boolean @default(true)
receiptWebhookEnabled Boolean @default(true)
uplinkWebhookEnabled Boolean @default(true)
uplinkQueryEnabled Boolean @default(true)
credentialSelfServiceEnabled Boolean @default(true)
qpsLimit Int @default(10)
timestampToleranceSeconds Int @default(300)
maxCredentialCount Int @default(2)
uplinkRetentionDays Int @default(90)
maxQueryRangeDays Int @default(31)
maxPageSize Int @default(100)
receiptDeliveryMode String @default("http")
uplinkDeliveryMode String @default("http")
webhookRetryEnabled Boolean @default(true)
webhookMaxAttempts Int @default(7)
webhookTimeoutSeconds Int @default(10)
requireHttps Boolean @default(true)
allowClientManualRetry Boolean @default(true)
allowClientTest Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
}
model SmsApplicationDailyUsage {
id String @id @default(cuid())
applicationId String
usageDate DateTime @db.Date
usedCount Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
@@unique([applicationId, usageDate])
@@index([usageDate])
}
model SmsApplicationHttpIpAllowlist {
id String @id @default(cuid())
applicationId String
ipCidr String
remark String?
createdAt DateTime @default(now())
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
@@unique([applicationId, ipCidr])
}
model HttpApiCredential {
id String @id @default(cuid())
applicationId String
name String
accessKey String @unique
secretEncrypted String
secretLast4 String
status String @default("active")
expiresAt DateTime?
lastUsedAt DateTime?
lastUsedIp String?
createdById String?
revokedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
requests OpenApiRequest[]
@@index([applicationId, status])
}
model OpenApiRequest {
id String @id @default(cuid())
tenantId String
applicationId String
credentialId String
requestId String @unique
idempotencyKey String
bodyHash String
clientMessageId String?
messageRecordId String?
httpStatus Int?
businessCode String?
responseBody Json?
sourceIp String?
userAgent String?
durationMs Int?
status String @default("processing")
completedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
credential HttpApiCredential @relation(fields: [credentialId], references: [id])
@@unique([applicationId, idempotencyKey])
@@index([applicationId, createdAt])
}
model HttpWebhookEndpoint {
id String @id @default(cuid())
applicationId String
eventType String
url String
secretEncrypted String
secretLast4 String
status String @default("active")
lastTestAt DateTime?
lastTestStatus String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
deliveries HttpWebhookDelivery[]
@@unique([applicationId, eventType])
}
model HttpWebhookEvent {
id String @id @default(cuid())
eventId String @unique
tenantId String
applicationId String
eventType String
messageRecordId String?
messageId String?
uplinkMessageId String?
payload Json
createdAt DateTime @default(now())
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
deliveries HttpWebhookDelivery[]
@@index([applicationId, createdAt])
}
model HttpWebhookDelivery {
id String @id @default(cuid())
eventId String
endpointId String
status String @default("pending")
attemptCount Int @default(0)
nextRetryAt DateTime?
lastHttpStatus Int?
lastError String?
deliveredAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
event HttpWebhookEvent @relation(fields: [eventId], references: [id], onDelete: Cascade)
endpoint HttpWebhookEndpoint @relation(fields: [endpointId], references: [id], onDelete: Cascade)
attempts HttpWebhookAttempt[]
@@unique([eventId, endpointId])
@@index([status, nextRetryAt])
}
model HttpWebhookAttempt {
id String @id @default(cuid())
deliveryId String
attemptNo Int
requestHeaders Json?
responseStatus Int?
responseSummary String?
errorMessage String?
durationMs Int?
createdAt DateTime @default(now())
delivery HttpWebhookDelivery @relation(fields: [deliveryId], references: [id], onDelete: Cascade)
@@unique([deliveryId, attemptNo])
}
model SmsSignature {
id String @id @default(cuid())
tenantId String
applicationId String?
name String
purpose String?
drainageInfo Json?
auditStatus String @default("draft")
reportStatus String @default("waiting_material")
rejectReason String?
materialVersion Int @default(1)
pendingReport Boolean @default(true)
reportChangedAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication? @relation(fields: [applicationId], references: [id])
materials SignatureMaterial[]
templates SmsTemplate[]
reportMaterials SignatureReportMaterial[]
drainageReportMaterials DrainageReportMaterial[]
drainageItems SmsDrainageInfo[]
reportTasks ChannelSignatureReportTask[]
messageRecords SmsMessageRecord[]
reportBatchItems ReportMaterialBatchItem[]
@@index([tenantId, auditStatus])
@@index([tenantId, updatedAt])
@@index([tenantId, reportStatus])
}
model SmsDrainageInfo {
id String @id @default(cuid())
tenantId String
signatureId String
applicationId String?
siteName String
url String
remark String?
reportValues Json?
auditStatus String @default("pending")
rejectReason String?
materialVersion Int @default(1)
pendingReport Boolean @default(true)
reportChangedAt DateTime @default(now())
submittedAt DateTime @default(now())
reviewedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
signature SmsSignature @relation(fields: [signatureId], references: [id], onDelete: Cascade)
application SmsApplication? @relation(fields: [applicationId], references: [id])
reportTasks ChannelSignatureReportTask[]
messageRecords SmsMessageRecord[]
reportBatchItems ReportMaterialBatchItem[]
@@index([tenantId, auditStatus, updatedAt])
@@index([signatureId, auditStatus])
@@index([applicationId])
}
model SignatureMaterial {
id String @id @default(cuid())
signatureId String
fileObjectId String?
materialType String
title String
description String?
createdAt DateTime @default(now())
signature SmsSignature @relation(fields: [signatureId], references: [id], onDelete: Cascade)
}
model SmsTemplate {
id String @id @default(cuid())
tenantId String
applicationId String
signatureId String?
name String
content String
category String?
auditStatus String @default("draft")
rejectReason String?
billingUnits Int @default(1)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication @relation(fields: [applicationId], references: [id])
signature SmsSignature? @relation(fields: [signatureId], references: [id])
variables TemplateVariable[]
sendTasks SmsSendTask[]
batchTasks SmsBatchTask[]
messageRecords SmsMessageRecord[]
@@index([tenantId, auditStatus, createdAt])
@@index([tenantId, auditStatus])
@@index([applicationId])
}
model TemplateVariable {
id String @id @default(cuid())
templateId String
name String
example String?
required Boolean @default(true)
createdAt DateTime @default(now())
template SmsTemplate @relation(fields: [templateId], references: [id], onDelete: Cascade)
@@unique([templateId, name])
}
model AuditRecord {
id String @id @default(cuid())
tenantId String?
targetType String
targetId String
action String
statusBefore String?
statusAfter String
reason String?
reviewerId String?
createdAt DateTime @default(now())
tenant Tenant? @relation(fields: [tenantId], references: [id])
reviewer User? @relation(fields: [reviewerId], references: [id])
@@index([targetType, targetId])
@@index([tenantId, createdAt])
}
model SmsChannel {
id String @id @default(cuid())
code String @unique
name String
carrier String?
carriers String[] @default([])
sendRegion String @default("全国")
protocol String @default("CMPP")
gatewayHost String
gatewayPort Int
enterpriseCode String?
account String
passwordCipher String
srcId String
cmppVersion String @default("2.0")
rateLimitPerSecond Int @default(100)
unitPrice BigInt @default(0)
status String @default("active")
config Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
groupItems SmsChannelGroupItem[]
routeRules ChannelRouteRule[]
healthMetrics ChannelHealthMetric[]
reportFields ChannelReportField[]
drainageReportMaterials DrainageReportMaterial[]
reportTasks ChannelSignatureReportTask[]
reportRecords ChannelSignatureReportRecord[]
messageRecords SmsMessageRecord[]
submitSessions CmppSubmitSession[]
submitRecords SmsSubmitRecord[]
receiptRecords SmsReceiptRecord[]
segmentAudits SmsMessageSegmentAudit[]
uplinkMessages SmsUplinkMessage[]
connectionStates CmppConnectionState[]
gatewaySubmitDeadLetters GatewaySubmitDeadLetter[]
receiptAnomalies SmsReceiptAnomaly[]
@@index([status])
@@index([status, createdAt])
}
model CmppConnectionState {
id String @id @default(cuid())
tenantId String?
applicationId String?
channelId String
connectionId String
status String @default("disconnected")
desiredConnections Int @default(1)
currentConnections Int @default(0)
lastConnectedAt DateTime?
lastDisconnectedAt DateTime?
lastHeartbeatAt DateTime?
reconnectCount Int @default(0)
lastReconnectAttemptAt DateTime?
nextReconnectAt DateTime?
lastErrorCategory String?
lastError String?
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
tenant Tenant? @relation(fields: [tenantId], references: [id])
application SmsApplication? @relation(fields: [applicationId], references: [id])
channel SmsChannel @relation(fields: [channelId], references: [id])
@@unique([applicationId, channelId, connectionId])
@@index([tenantId, status])
@@index([applicationId, status])
@@index([channelId, status])
@@index([status, nextReconnectAt])
}
model CmppDownstreamConnection {
id String @id @default(cuid())
tenantId String
applicationId String
account String
enterpriseCode String
connectionId String @unique
remoteIp String?
protocol String?
status String @default("connected")
connectedAt DateTime @default(now())
lastHeartbeatAt DateTime?
lastSubmitAt DateTime?
lastDeliverAt DateTime?
disconnectedAt DateTime?
lastError String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication @relation(fields: [applicationId], references: [id])
@@index([applicationId, status, updatedAt])
@@index([tenantId, status, updatedAt])
@@index([account, status])
}
model SmsChannelGroup {
id String @id @default(cuid())
code String @unique
name String
description String?
carrier String @default("mobile")
status String @default("active")
retryEnabled Boolean @default(true)
retryTimeLimitHours Int @default(72)
retryTimeLimitMinutes Int @default(720)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
items SmsChannelGroupItem[]
routeRules ChannelRouteRule[]
submitRecords SmsSubmitRecord[]
}
model SmsChannelGroupItem {
id String @id @default(cuid())
groupId String
channelId String
carrier String?
province String?
priority Int @default(100)
weight Int @default(1)
isBackup Boolean @default(false)
createdAt DateTime @default(now())
group SmsChannelGroup @relation(fields: [groupId], references: [id], onDelete: Cascade)
channel SmsChannel @relation(fields: [channelId], references: [id])
@@unique([groupId, channelId, carrier, province])
@@unique([groupId, province])
@@index([channelId])
}
model ChannelRouteRule {
id String @id @default(cuid())
tenantId String?
applicationId String?
groupId String
channelId String?
carrier String?
province String?
priority Int @default(100)
status String @default("active")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
group SmsChannelGroup @relation(fields: [groupId], references: [id])
channel SmsChannel? @relation(fields: [channelId], references: [id])
@@index([tenantId, applicationId, status])
@@index([groupId, priority])
}
model ChannelHealthMetric {
id String @id @default(cuid())
channelId String
submitTotal Int @default(0)
submitSuccess Int @default(0)
submitFailed Int @default(0)
receiptSuccess Int @default(0)
receiptFailed Int @default(0)
unknownTotal Int @default(0)
windowStart DateTime
windowEnd DateTime
createdAt DateTime @default(now())
channel SmsChannel @relation(fields: [channelId], references: [id])
@@index([channelId, windowStart])
}
model ChannelReportField {
id String @id @default(cuid())
channelId String
drainageFieldId String?
reportType String @default("both")
code String
name String
exportName String?
fieldType String
required Boolean @default(false)
description String?
sortOrder Int @default(100)
columnWidth Int @default(18)
imageWidth Int @default(120)
imageHeight Int @default(80)
defaultValue String?
transform String?
status String @default("active")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
channel SmsChannel @relation(fields: [channelId], references: [id], onDelete: Cascade)
drainageField DrainageField? @relation(fields: [drainageFieldId], references: [id])
@@unique([channelId, code, reportType])
@@index([drainageFieldId, reportType])
}
model CommonReportField {
id String @id @default(cuid())
drainageFieldId String
reportType String
required Boolean @default(false)
sortOrder Int @default(100)
status String @default("active")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
drainageField DrainageField @relation(fields: [drainageFieldId], references: [id], onDelete: Restrict)
@@unique([drainageFieldId, reportType])
@@index([reportType, status, sortOrder])
}
model SignatureReportMaterial {
id String @id @default(cuid())
signatureId String
channelId String
fieldCode String
fieldValue String?
fileObjectId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
signature SmsSignature @relation(fields: [signatureId], references: [id], onDelete: Cascade)
@@unique([signatureId, channelId, fieldCode])
@@index([channelId])
}
model DrainageReportMaterial {
id String @id @default(cuid())
signatureId String
drainageItemId String
channelId String
fieldCode String
fieldValue String?
fileObjectId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
signature SmsSignature @relation(fields: [signatureId], references: [id], onDelete: Cascade)
channel SmsChannel @relation(fields: [channelId], references: [id], onDelete: Cascade)
@@unique([signatureId, drainageItemId, channelId, fieldCode])
@@index([channelId, fieldCode])
}
model ChannelSignatureReportTask {
id String @id @default(cuid())
tenantId String
signatureId String
channelId String
carrier String?
approvedAt DateTime?
approvalScope String @default("legacy_channel")
reportType String @default("signature")
drainageItemId String?
status String @default("pending")
reason String?
createdById String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
signature SmsSignature @relation(fields: [signatureId], references: [id])
channel SmsChannel @relation(fields: [channelId], references: [id])
drainageInfo SmsDrainageInfo? @relation(fields: [drainageItemId], references: [id])
records ChannelSignatureReportRecord[]
exportFiles ReportExportFile[]
receiptImports ReportReceiptImport[]
exportItems ReportExportFileItem[]
@@index([tenantId, status])
@@index([status, createdAt])
@@index([signatureId, channelId])
@@index([signatureId, channelId, carrier])
@@index([signatureId, drainageItemId, channelId])
@@index([reportType, status])
}
model SignatureRetirementRule {
id String @id @default(cuid())
ruleType String
targetId String?
targetKey String @default("")
enabled Boolean @default(true)
mobileWindowDays Int @default(30)
mobileThreshold Int @default(1)
unicomWindowDays Int @default(30)
unicomThreshold Int @default(1)
telecomWindowDays Int @default(30)
telecomThreshold Int @default(1)
messageTemplate String?
version Int @default(1)
createdById String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([ruleType, targetKey])
@@index([ruleType, enabled])
}
model SignatureRetirementWebhook {
id String @id @default(cuid())
name String
platform String
urlEncrypted String
urlMasked String
status String @default("active")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status, createdAt])
}
model SignatureRetirementCycle {
id String @id @default(cuid())
dimensionType String
signatureId String
channelId String?
channelKey String @default("")
carrier String
status String @default("open")
startedOn DateTime @db.Date
lastDetectedOn DateTime @db.Date
resolvedOn DateTime? @db.Date
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([dimensionType, signatureId, channelKey, carrier, status])
@@index([status, lastDetectedOn])
}
model SignatureRetirementDetection {
id String @id @default(cuid())
detectionDate DateTime @db.Date
dimensionType String
tenantId String
applicationId String?
signatureId String
channelId String?
channelKey String @default("")
carrier String
windowDays Int
threshold Int
submittedAttempts Int @default(0)
acceptedBusinessCount Int @default(0)
deliveredBusinessCount Int @default(0)
approvedAt DateTime
ruleId String?
ruleVersion Int @default(1)
status String
cycleId String?
suppressed Boolean @default(false)
notificationTitle String?
notificationContent String?
createdAt DateTime @default(now())
@@unique([detectionDate, dimensionType, signatureId, channelKey, carrier])
@@index([detectionDate, dimensionType, status])
@@index([signatureId, carrier, detectionDate])
@@index([channelId, carrier, detectionDate])
}
model SignatureRetirementMessage {
id String @id @default(cuid())
detectionId String @unique
cycleId String
tenantId String
title String
content String
isRead Boolean @default(false)
suppressed Boolean @default(false)
readAt DateTime?
createdAt DateTime @default(now())
@@index([createdAt, isRead, suppressed])
@@index([tenantId, createdAt])
}
model SignatureRetirementSuppression {
id String @id @default(cuid())
dimensionType String
signatureId String
channelId String?
channelKey String @default("")
carrier String
mode String
muteUntil DateTime? @db.Date
active Boolean @default(true)
reason String?
operatorId String?
cancelledAt DateTime?
cancelledById String?
cancelReason String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([dimensionType, signatureId, channelKey, carrier])
@@index([active, muteUntil])
}
model SignatureRetirementWebhookDelivery {
id String @id @default(cuid())
detectionDate DateTime @db.Date
webhookId String
groupKey String
payload Json
status String @default("pending")
attemptCount Int @default(0)
nextRetryAt DateTime?
lastHttpStatus Int?
lastError String?
deliveredAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([webhookId, detectionDate, groupKey])
@@index([status, nextRetryAt])
}
model ChannelSignatureReportRecord {
id String @id @default(cuid())
taskId String
channelId String
action String
statusBefore String?
statusAfter String
reason String?
operatorId String?
sourceEntry String @default("system")
createdAt DateTime @default(now())
task ChannelSignatureReportTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
channel SmsChannel @relation(fields: [channelId], references: [id])
@@index([taskId, createdAt])
@@index([channelId, createdAt])
@@index([createdAt])
}
model ReportExportFile {
id String @id @default(cuid())
taskId String?
batchId String?
channelId String?
fileObjectId String?
fileName String
rowCount Int @default(0)
status String @default("generated")
createdAt DateTime @default(now())
task ChannelSignatureReportTask? @relation(fields: [taskId], references: [id], onDelete: Cascade)
batch ReportMaterialBatch? @relation(fields: [batchId], references: [id], onDelete: Cascade)
items ReportExportFileItem[]
@@index([batchId, channelId])
}
model ReportMaterialBatch {
id String @id @default(cuid())
batchNo String @unique
status String @default("generating")
createdById String?
selectedCount Int @default(0)
channelCount Int @default(0)
fileCount Int @default(0)
errorMessage String?
createdAt DateTime @default(now())
completedAt DateTime?
items ReportMaterialBatchItem[]
exportFiles ReportExportFile[]
@@index([status, createdAt])
}
model ReportMaterialBatchItem {
id String @id @default(cuid())
batchId String
signatureId String
drainageItemId String?
reportType String
materialVersion Int
snapshot Json
createdAt DateTime @default(now())
batch ReportMaterialBatch @relation(fields: [batchId], references: [id], onDelete: Cascade)
signature SmsSignature @relation(fields: [signatureId], references: [id])
drainageInfo SmsDrainageInfo? @relation(fields: [drainageItemId], references: [id])
exportItems ReportExportFileItem[]
@@index([batchId, reportType])
@@index([signatureId, drainageItemId])
}
model ReportExportFileItem {
id String @id @default(cuid())
exportFileId String
batchItemId String
taskId String
rowNumber Int
exportFile ReportExportFile @relation(fields: [exportFileId], references: [id], onDelete: Cascade)
batchItem ReportMaterialBatchItem @relation(fields: [batchItemId], references: [id], onDelete: Cascade)
task ChannelSignatureReportTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
@@unique([exportFileId, batchItemId])
@@index([taskId])
}
model ReportMaterialImportProfile {
id String @id @default(cuid())
name String
reportType String
tenantId String?
applicationId String?
sheetName String?
headerRowCount Int @default(1)
dataStartRow Int @default(2)
status String @default("active")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
columns ReportMaterialImportProfileColumn[]
@@index([reportType, status])
@@index([tenantId, applicationId])
}
model ReportMaterialImportProfileColumn {
id String @id @default(cuid())
profileId String
sourceHeader String
sourceHeaderPath String?
sourceColumnIndex Int
targetFieldCode String
targetKind String @default("dynamic")
fieldType String @default("string")
required Boolean @default(false)
transform String?
sortOrder Int @default(100)
createdAt DateTime @default(now())
profile ReportMaterialImportProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
@@unique([profileId, sourceColumnIndex])
@@index([profileId, sortOrder])
}
model ReportMaterialImportBatch {
id String @id @default(cuid())
tenantId String
applicationId String?
profileId String?
fileObjectId String
fileName String
reportType String
status String @default("analyzed")
sheetName String
headerRowCount Int @default(1)
dataStartRow Int @default(2)
mapping Json
preview Json?
result Json?
rowCount Int @default(0)
successCount Int @default(0)
failedCount Int @default(0)
reviewedById String?
reviewedAt DateTime?
createdAt DateTime @default(now())
completedAt DateTime?
items ReportMaterialImportItem[]
@@index([tenantId, createdAt])
@@index([status, createdAt])
}
model ReportMaterialImportItem {
id String @id @default(cuid())
batchId String
rowNumber Int
reportType String
operation String
targetId String?
status String @default("pending_review")
payload Json
originalSnapshot Json?
errorMessage String?
reviewReason String?
reviewedById String?
reviewedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
batch ReportMaterialImportBatch @relation(fields: [batchId], references: [id], onDelete: Cascade)
@@unique([batchId, rowNumber])
@@index([batchId, status])
@@index([reportType, status, createdAt])
@@index([targetId])
}
model ReportReceiptImport {
id String @id @default(cuid())
taskId String
fileObjectId String?
fileName String
rowCount Int @default(0)
successCount Int @default(0)
failedCount Int @default(0)
status String @default("imported")
result Json?
createdAt DateTime @default(now())
task ChannelSignatureReportTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
}
model RiskRule {
id String @id @default(cuid())
tenantId String?
applicationId String?
code String
name String
description String?
metric String
thresholdValue Float
action String @default("manual_review")
status String @default("active")
priority Int @default(100)
config Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant? @relation(fields: [tenantId], references: [id])
application SmsApplication? @relation(fields: [applicationId], references: [id], onDelete: Cascade)
hits RiskHitRecord[]
phoneFrequencyStates PhoneFrequencyState[]
phoneFrequencyHits PhoneFrequencyHit[]
@@unique([applicationId, code])
@@index([tenantId, applicationId, status, priority])
}
model SmsSendTask {
id String @id @default(cuid())
tenantId String
applicationId String?
templateId String?
taskNo String @unique
sourceType String @default("risk")
aggregationKey String? @unique
contentHash String?
windowStartedAt DateTime?
windowEndsAt DateTime?
content String
category String?
phoneTotal Int
uniquePhoneTotal Int
duplicateRatio Float @default(0)
illegalRatio Float @default(0)
blacklistHitRatio Float @default(0)
variableIssues Json?
status String @default("created")
riskDecision String @default("allow")
reviewReason String?
rejectReason String?
createdById String?
reviewedById String?
reviewedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication? @relation(fields: [applicationId], references: [id])
template SmsTemplate? @relation(fields: [templateId], references: [id])
createdBy User? @relation("SmsSendTaskCreator", fields: [createdById], references: [id])
reviewedBy User? @relation("SmsSendTaskReviewer", fields: [reviewedById], references: [id])
riskHits RiskHitRecord[]
messageRecords SmsMessageRecord[]
@@index([tenantId, status, createdAt])
@@index([applicationId, createdAt])
}
model RiskHitRecord {
id String @id @default(cuid())
tenantId String
taskId String
ruleId String?
ruleCode String
ruleName String
thresholdValue Float
actualValue Float
action String
reason String
createdAt DateTime @default(now())
tenant Tenant @relation(fields: [tenantId], references: [id])
task SmsSendTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
rule RiskRule? @relation(fields: [ruleId], references: [id])
@@index([tenantId, createdAt])
@@index([taskId])
@@index([ruleCode])
}
model PhoneFrequencyState {
id String @id @default(cuid())
tenantId String
applicationId String
ruleId String?
ruleCode String
phoneNumber String
windowStartedAt DateTime
windowEndsAt DateTime
count Int @default(0)
generation Int @default(0)
activeHitId String? @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
rule RiskRule? @relation(fields: [ruleId], references: [id], onDelete: SetNull)
activeHit PhoneFrequencyHit? @relation("ActivePhoneFrequencyHit", fields: [activeHitId], references: [id], onDelete: SetNull)
@@unique([applicationId, ruleCode, phoneNumber])
@@index([tenantId, applicationId, windowEndsAt])
@@index([ruleId])
}
model PhoneFrequencyHit {
id String @id @default(cuid())
tenantId String
applicationId String
ruleId String?
ruleCode String
ruleName String
phoneNumber String
thresholdValue Int
actualValue Int
windowStartedAt DateTime
windowEndsAt DateTime
generation Int @default(0)
action String @default("block")
sourceType String?
releasedAt DateTime?
releasedById String?
releaseReason String?
createdAt DateTime @default(now())
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication @relation(fields: [applicationId], references: [id])
rule RiskRule? @relation(fields: [ruleId], references: [id], onDelete: SetNull)
releasedBy User? @relation("PhoneFrequencyHitReleaser", fields: [releasedById], references: [id])
activeForState PhoneFrequencyState? @relation("ActivePhoneFrequencyHit")
@@unique([applicationId, ruleCode, phoneNumber, windowStartedAt, generation])
@@index([tenantId, applicationId, createdAt])
@@index([applicationId, phoneNumber, createdAt])
@@index([windowEndsAt, releasedAt])
@@index([ruleId])
@@index([releasedById])
}
model PhoneFrequencyWhitelist {
id String @id @default(cuid())
phoneNumber String @unique
status String @default("active")
reason String
remark String?
createdById String
updatedById String
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdBy User @relation("PhoneFrequencyWhitelistCreator", fields: [createdById], references: [id])
updatedBy User @relation("PhoneFrequencyWhitelistUpdater", fields: [updatedById], references: [id])
@@index([status, updatedAt])
@@index([createdById])
@@index([updatedById])
}
model SmsBatchTask {
id String @id @default(cuid())
tenantId String
applicationId String?
templateId String?
taskNo String @unique
sourceType String @default("client")
content String
category String?
phoneTotal Int
status String @default("created")
riskTaskId String?
auditStatus String @default("approved")
reviewReason String?
rejectReason String?
progressTotal Int @default(0)
submittedTotal Int @default(0)
successTotal Int @default(0)
failedTotal Int @default(0)
unknownTotal Int @default(0)
timeoutTotal Int @default(0)
scheduledAt DateTime?
canceledAt DateTime?
createdById String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication? @relation(fields: [applicationId], references: [id])
template SmsTemplate? @relation(fields: [templateId], references: [id])
createdBy User? @relation("SmsBatchTaskCreator", fields: [createdById], references: [id])
apiRequests SmsApiRequest[]
messages SmsMessageRecord[]
submitRecords SmsSubmitRecord[]
receiptRecords SmsReceiptRecord[]
segmentAudits SmsMessageSegmentAudit[]
@@index([tenantId, status, createdAt])
@@index([status, scheduledAt])
@@index([applicationId, createdAt])
}
model SmsApiRequest {
id String @id @default(cuid())
tenantId String
batchTaskId String
requestId String @unique
sourceIp String?
userAgent String?
payloadSummary Json?
status String @default("accepted")
createdAt DateTime @default(now())
tenant Tenant @relation(fields: [tenantId], references: [id])
batchTask SmsBatchTask @relation(fields: [batchTaskId], references: [id], onDelete: Cascade)
@@index([tenantId, createdAt])
@@index([batchTaskId])
}
model SmsMessageRecord {
id String @id @default(cuid())
tenantId String?
batchTaskId String?
applicationId String?
templateId String?
signatureId String?
drainageInfoId String?
reviewTaskId String?
messageId String @unique
clientMessageId String?
phoneNumber String
carrier String?
province String?
content String
hasDrainageContent Boolean?
drainageDetection Json?
drainageDetectionVersion String?
drainageEvaluatedAt DateTime?
billingUnits Int @default(1)
unitPrice BigInt @default(0)
amountCents BigInt @default(0)
queuePriority String @default("normal")
channelId String?
submitId String?
gatewayMessageId String?
cmppSubmitSequenceId String?
cmppSubmitGroupMessageId String?
cmppRegisteredDelivery Boolean?
clientSrcId String?
applicationExtension String?
status String @default("queued")
submitStatus String?
receiptStatus String?
receiptRawStatus String?
errorCode String?
errorMessage String?
queuedAt DateTime @default(now())
submittedAt DateTime?
deliveredAt DateTime?
timeoutAt DateTime?
timeoutReceiptQueuedAt DateTime?
updatedAt DateTime @updatedAt
tenant Tenant? @relation(fields: [tenantId], references: [id])
batchTask SmsBatchTask? @relation(fields: [batchTaskId], references: [id], onDelete: Cascade)
application SmsApplication? @relation(fields: [applicationId], references: [id])
template SmsTemplate? @relation(fields: [templateId], references: [id])
signature SmsSignature? @relation(fields: [signatureId], references: [id])
drainageInfo SmsDrainageInfo? @relation(fields: [drainageInfoId], references: [id])
reviewTask SmsSendTask? @relation(fields: [reviewTaskId], references: [id])
channel SmsChannel? @relation(fields: [channelId], references: [id])
submitRecords SmsSubmitRecord[]
receiptRecords SmsReceiptRecord[]
segmentAudits SmsMessageSegmentAudit[]
receiptAnomalies SmsReceiptAnomaly[]
matchedUplinks SmsUplinkMessage[] @relation("SmsUplinkMatchedMessage")
uplinkMatchCandidates SmsUplinkMatchCandidate[]
downstreamDeliveries CmppDownstreamDelivery[]
@@unique([applicationId, clientMessageId])
@@index([tenantId, status, queuedAt])
@@index([queuedAt])
@@index([batchTaskId, status])
@@index([reviewTaskId, status])
@@index([phoneNumber])
@@index([gatewayMessageId])
@@index([drainageInfoId, queuedAt])
@@index([hasDrainageContent, queuedAt])
}
model CmppSubmitSession {
id String @id @default(cuid())
channelId String
sessionNo String @unique
status String @default("open")
submitTotal Int @default(0)
acceptedTotal Int @default(0)
rejectedTotal Int @default(0)
startedAt DateTime @default(now())
endedAt DateTime?
channel SmsChannel @relation(fields: [channelId], references: [id])
submitRecords SmsSubmitRecord[]
@@index([channelId, status])
}
model SmsSubmitRecord {
id String @id @default(cuid())
tenantId String?
batchTaskId String?
messageRecordId String
channelId String
channelGroupId String?
channelGroupName String?
sessionId String?
retryOfSubmitRecordId String? @unique
submitId String @unique
sequenceId Int?
gatewayMessageId String?
submitStatus String @default("queued")
costUnitPrice BigInt @default(0)
costAmountCents BigInt @default(0)
errorCode String?
errorMessage String?
submittedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant? @relation(fields: [tenantId], references: [id])
batchTask SmsBatchTask? @relation(fields: [batchTaskId], references: [id], onDelete: Cascade)
messageRecord SmsMessageRecord @relation(fields: [messageRecordId], references: [id], onDelete: Cascade)
channel SmsChannel @relation(fields: [channelId], references: [id])
channelGroup SmsChannelGroup? @relation(fields: [channelGroupId], references: [id], onDelete: SetNull)
session CmppSubmitSession? @relation(fields: [sessionId], references: [id])
retryOfSubmitRecord SmsSubmitRecord? @relation("SmsSubmitRetry", fields: [retryOfSubmitRecordId], references: [id])
retrySubmit SmsSubmitRecord? @relation("SmsSubmitRetry")
segmentAudits SmsMessageSegmentAudit[]
receiptAnomalies SmsReceiptAnomaly[]
@@index([tenantId, createdAt])
@@index([messageRecordId])
@@index([gatewayMessageId])
@@index([channelId, gatewayMessageId])
@@index([channelGroupId])
}
model DailyReconciliationReport {
id String @id @default(cuid())
reportDate DateTime @db.Date
tenantId String
tenantName String
applicationId String
applicationName String
submittedUnits Int @default(0)
sentUnits Int @default(0)
unknownUnits Int @default(0)
successUnits Int @default(0)
failedUnits Int @default(0)
generatedAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([reportDate, tenantId, applicationId])
@@index([reportDate])
@@index([tenantId, reportDate])
@@index([applicationId, reportDate])
}
model DailyProfitReport {
id String @id @default(cuid())
reportDate DateTime @db.Date
dimensionType String
dimensionId String
dimensionName String
tenantId String?
tenantName String?
applicationId String?
channelId String?
submittedUnits Int @default(0)
sentUnits Int @default(0)
unknownUnits Int @default(0)
successUnits Int @default(0)
failedUnits Int @default(0)
revenueCents BigInt @default(0)
refundCents BigInt @default(0)
costCents BigInt @default(0)
profitCents BigInt @default(0)
profitRateBps Int @default(0)
generatedAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([reportDate, dimensionType, dimensionId])
@@index([dimensionType, reportDate])
@@index([tenantId, reportDate])
@@index([applicationId, reportDate])
@@index([channelId, reportDate])
}
model DailyQualityReport {
id String @id @default(cuid())
reportDate DateTime @db.Date
dimensionType String
dimensionId String
dimensionName String
tenantId String?
tenantName String?
applicationId String?
channelId String?
signatureId String?
drainageInfoId String?
submittedUnits Int @default(0)
sentUnits Int @default(0)
unknownUnits Int @default(0)
successUnits Int @default(0)
failedUnits Int @default(0)
successRateBps Int @default(0)
avgArrivalMs Int?
generatedAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([reportDate, dimensionType, dimensionId])
@@index([dimensionType, reportDate, sentUnits])
@@index([tenantId, reportDate])
@@index([applicationId, reportDate])
@@index([channelId, reportDate])
@@index([signatureId, reportDate])
@@index([drainageInfoId, reportDate])
}
model SmsMessageSegmentAudit {
id String @id @default(cuid())
tenantId String?
batchTaskId String?
messageRecordId String
submitRecordId String?
channelId String?
submitId String
attempt Int @default(0)
segmentTotal Int @default(1)
segmentIndex Int @default(1)
sequenceId Int?
gatewayMessageId String?
submitStatus String @default("queued")
receiptStatus String?
rawStatus String?
compensationType String?
errorCode String?
errorMessage String?
submittedAt DateTime?
deliveredAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant? @relation(fields: [tenantId], references: [id])
batchTask SmsBatchTask? @relation(fields: [batchTaskId], references: [id])
messageRecord SmsMessageRecord @relation(fields: [messageRecordId], references: [id], onDelete: Cascade)
submitRecord SmsSubmitRecord? @relation(fields: [submitRecordId], references: [id])
channel SmsChannel? @relation(fields: [channelId], references: [id])
@@unique([messageRecordId, submitId, segmentIndex])
@@index([tenantId, createdAt])
@@index([messageRecordId, segmentIndex])
@@index([submitRecordId])
@@index([gatewayMessageId])
@@index([submitStatus, receiptStatus])
}
model CmppInboundLongMessage {
id String @id @default(cuid())
tenantId String
applicationId String
groupKey String
account String
srcId String?
phoneNumbers Json
concatReference Int
segmentTotal Int
msgFmt Int
messageId String @unique
status String @default("collecting")
response Json?
expiresAt DateTime
completedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
segments CmppInboundLongMessageSegment[]
@@index([groupKey, status, createdAt])
@@index([applicationId, status, expiresAt])
}
model CmppInboundLongMessageSegment {
id String @id @default(cuid())
groupId String
segmentIndex Int
sequenceId String?
registeredDelivery Boolean @default(true)
content String
contentHash String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
group CmppInboundLongMessage @relation(fields: [groupId], references: [id], onDelete: Cascade)
@@unique([groupId, segmentIndex])
@@index([sequenceId])
}
model SmsReceiptRecord {
id String @id @default(cuid())
tenantId String?
batchTaskId String?
messageRecordId String?
receiptKey String @unique
channelId String?
messageId String
gatewayMessageId String
phoneNumber String?
sequenceId Int?
receiptStatus String
rawStatus String
errorCode String?
errorMessage String?
deliveredAt DateTime
createdAt DateTime @default(now())
tenant Tenant? @relation(fields: [tenantId], references: [id])
batchTask SmsBatchTask? @relation(fields: [batchTaskId], references: [id])
messageRecord SmsMessageRecord? @relation(fields: [messageRecordId], references: [id])
channel SmsChannel? @relation(fields: [channelId], references: [id])
anomalies SmsReceiptAnomaly[]
@@index([tenantId, createdAt])
@@index([messageId])
@@index([gatewayMessageId])
@@index([channelId, gatewayMessageId])
@@index([channelId, gatewayMessageId, phoneNumber])
}
model SmsReceiptAnomaly {
id String @id @default(cuid())
anomalyKey String @unique
tenantId String?
applicationId String?
channelId String?
messageRecordId String?
submitRecordId String?
receiptRecordId String?
anomalyType String
status String @default("pending")
previousStatus String?
incomingStatus String?
rawStatus String?
errorCode String?
detail Json?
occurrenceCount Int @default(1)
firstOccurredAt DateTime @default(now())
lastOccurredAt DateTime @default(now())
resolvedAt DateTime?
resolutionNote String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: SetNull)
application SmsApplication? @relation(fields: [applicationId], references: [id], onDelete: SetNull)
channel SmsChannel? @relation(fields: [channelId], references: [id], onDelete: SetNull)
messageRecord SmsMessageRecord? @relation(fields: [messageRecordId], references: [id], onDelete: SetNull)
submitRecord SmsSubmitRecord? @relation(fields: [submitRecordId], references: [id], onDelete: SetNull)
receiptRecord SmsReceiptRecord? @relation(fields: [receiptRecordId], references: [id], onDelete: SetNull)
@@index([status, lastOccurredAt])
@@index([lastOccurredAt])
@@index([anomalyType, lastOccurredAt])
@@index([tenantId, status, lastOccurredAt])
@@index([applicationId, status, lastOccurredAt])
@@index([channelId, status, lastOccurredAt])
@@index([messageRecordId])
@@index([submitRecordId])
}
model SmsUplinkMessage {
id String @id @default(cuid())
tenantId String?
applicationId String?
channelId String
messageRecordId String?
messageId String?
sequenceId Int?
phoneNumber String
destId String
content String
matchStatus String @default("unmatched")
matchReason String?
receivedAt DateTime
createdAt DateTime @default(now())
tenant Tenant? @relation(fields: [tenantId], references: [id])
application SmsApplication? @relation(fields: [applicationId], references: [id])
messageRecord SmsMessageRecord? @relation("SmsUplinkMatchedMessage", fields: [messageRecordId], references: [id])
channel SmsChannel @relation(fields: [channelId], references: [id])
matchCandidates SmsUplinkMatchCandidate[]
@@index([tenantId, createdAt])
@@index([receivedAt])
@@index([applicationId, receivedAt])
@@index([channelId, receivedAt])
@@index([messageRecordId])
@@index([matchStatus])
@@index([phoneNumber])
}
model SmsUplinkMatchCandidate {
id String @id @default(cuid())
uplinkMessageId String
tenantId String
applicationId String
messageRecordId String?
matchSource String
confidence Int @default(50)
reason String?
status String @default("pending")
claimedAt DateTime?
claimedById String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
uplinkMessage SmsUplinkMessage @relation(fields: [uplinkMessageId], references: [id], onDelete: Cascade)
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication @relation(fields: [applicationId], references: [id])
messageRecord SmsMessageRecord? @relation(fields: [messageRecordId], references: [id])
@@unique([uplinkMessageId, applicationId, messageRecordId])
@@index([uplinkMessageId, status])
@@index([tenantId, status, createdAt])
@@index([applicationId, status, createdAt])
@@index([messageRecordId])
}
model CmppDownstreamDelivery {
id String @id @default(cuid())
tenantId String
applicationId String
messageRecordId String?
messageId String?
dedupeKey String? @unique
deliveryType String
status String @default("pending")
payload Json
retryEnabled Boolean @default(true)
retryCount Int @default(0)
manualRetryCount Int @default(0)
lastRetriedAt DateTime?
nextRetryAt DateTime?
sentAt DateTime?
acknowledgedAt DateTime?
ackDeadlineAt DateTime?
ackResult Int?
ackSequenceId String?
ackMessageId String?
connectionId String?
deliveredAt DateTime?
lastError String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication @relation(fields: [applicationId], references: [id])
messageRecord SmsMessageRecord? @relation(fields: [messageRecordId], references: [id])
attempts CmppDownstreamDeliveryAttempt[]
requeueItems DownstreamRequeueTaskItem[]
@@index([tenantId, status, createdAt])
@@index([applicationId, status, createdAt])
@@index([messageId])
@@index([messageRecordId])
@@index([deliveryType, status])
@@index([status, ackDeadlineAt])
}
model DownstreamRequeueTask {
id String @id @default(cuid())
taskNo String @unique
tenantId String?
applicationId String?
status String @default("queued")
filterSnapshot Json
snapshotAt DateTime
reason String
ratePerSecond Int @default(10)
consecutiveFailureLimit Int @default(10)
totalCount Int @default(0)
successCount Int @default(0)
failedCount Int @default(0)
skippedCount Int @default(0)
waitingCount Int @default(0)
consecutiveFailures Int @default(0)
applicationFailures Json @default("{}")
lastError String?
scanLeaseOwner String?
scanLeaseUntil DateTime?
createdById String?
startedAt DateTime?
pausedAt DateTime?
finishedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant? @relation(fields: [tenantId], references: [id])
application SmsApplication? @relation(fields: [applicationId], references: [id])
createdBy User? @relation("DownstreamRequeueTaskCreator", fields: [createdById], references: [id])
items DownstreamRequeueTaskItem[]
@@index([status, createdAt])
@@index([applicationId, status, createdAt])
@@index([tenantId, createdAt])
}
model DownstreamRequeueRateWindow {
id String @id @default(cuid())
applicationId String
windowStartedAt DateTime
consumed Int @default(0)
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
@@unique([applicationId, windowStartedAt])
@@index([windowStartedAt])
}
model DownstreamRequeueTaskItem {
id String @id @default(cuid())
taskId String
deliveryId String
applicationId String
status String @default("queued")
previousStatus String
skipReason String?
errorMessage String?
claimedAt DateTime?
completedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
task DownstreamRequeueTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
delivery CmppDownstreamDelivery @relation(fields: [deliveryId], references: [id], onDelete: Restrict)
@@unique([taskId, deliveryId])
@@index([taskId, status, createdAt])
@@index([applicationId, status, createdAt])
@@index([deliveryId, status])
}
model CmppDownstreamDeliveryAttempt {
id String @id @default(cuid())
deliveryId String
attemptKey String @unique
attemptNo Int
connectionId String?
sequenceId String?
messageId String?
status String
sentAt DateTime?
ackDeadlineAt DateTime?
acknowledgedAt DateTime?
ackResult Int?
failureType String?
errorMessage String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
delivery CmppDownstreamDelivery @relation(fields: [deliveryId], references: [id], onDelete: Cascade)
@@index([deliveryId, attemptNo])
@@index([status, ackDeadlineAt])
@@index([connectionId, sequenceId])
}
model UpstreamReceiptInbox {
id String @id @default(cuid())
receiptKey String @unique
incomingChannelId String
incomingConnectionId String?
upstreamAccount String
upstreamHost String
upstreamPort Int
protocol String
protocolVersion String
provisionalMessageId String?
sequenceId Int?
gatewayMessageId String
phoneNumber String?
receiptStatus String
rawStatus String
errorCode String?
errorMessage String?
deliveredAt DateTime
receivedAt DateTime @default(now())
status String @default("pending")
matchedMessageRecordId String?
matchedSubmitRecordId String?
matchedChannelId String?
attemptCount Int @default(0)
nextRetryAt DateTime?
lastError String?
processedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status, nextRetryAt, receivedAt])
@@index([gatewayMessageId, phoneNumber])
@@index([incomingChannelId, receivedAt])
@@index([matchedMessageRecordId])
}
model GatewaySubmitDeadLetter {
id String @id @default(cuid())
streamMessageId String @unique
tenantId String?
applicationId String?
channelId String?
traceId String?
messageId String?
submitId String?
status String @default("pending")
failureCode String
failureMessage String
attempts Int @default(1)
maxAttempts Int @default(3)
commandPayload Json?
rawPayload String?
manualRetryCount Int @default(0)
lastRetryStreamId String?
lastRetriedAt DateTime?
resolvedAt DateTime?
resolvedStatus String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant? @relation(fields: [tenantId], references: [id])
application SmsApplication? @relation(fields: [applicationId], references: [id])
channel SmsChannel? @relation(fields: [channelId], references: [id])
@@index([status, createdAt])
@@index([tenantId, status, createdAt])
@@index([applicationId, status, createdAt])
@@index([channelId, status, createdAt])
@@index([messageId])
@@index([submitId])
}
model GatewayDownstreamRecoveryStatus {
id String @id @default(cuid())
account String @unique
tenantId String?
applicationId String?
gatewayInstanceId String?
state String
lockOwner String?
lockExpiresAt DateTime?
lastAttemptAt DateTime?
lastSuccessAt DateTime?
lastFailureAt DateTime?
nextRetryAt DateTime?
attemptCount Int @default(0)
failureCategory String?
lastError String?
lastSkipReason String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant? @relation(fields: [tenantId], references: [id])
application SmsApplication? @relation(fields: [applicationId], references: [id])
@@index([tenantId, state, updatedAt])
@@index([applicationId, state, updatedAt])
@@index([failureCategory, updatedAt])
@@index([lockOwner, lockExpiresAt])
@@index([state, updatedAt])
@@index([nextRetryAt])
}