Files
lislgosms/api/prisma/schema.prisma
T

1780 lines
56 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[]
smsBatchTasks SmsBatchTask[]
smsMessageRecords SmsMessageRecord[]
smsApiRequests SmsApiRequest[]
smsSubmitRecords SmsSubmitRecord[]
smsReceiptRecords SmsReceiptRecord[]
smsMessageSegmentAudits SmsMessageSegmentAudit[]
smsUplinkMessages SmsUplinkMessage[]
smsUplinkMatchCandidates SmsUplinkMatchCandidate[]
cmppDownstreamDeliveries CmppDownstreamDelivery[]
cmppDownstreamConnections CmppDownstreamConnection[]
cmppConnectionStates CmppConnectionState[]
gatewayDownstreamRecoveryStatuses GatewayDownstreamRecoveryStatus[]
gatewaySubmitDeadLetters GatewaySubmitDeadLetter[]
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 @unique
email String? @unique
phone String? @unique
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")
}
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 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 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
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])
}
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")
maxPhonesPerTask Int @default(1000000)
templateMismatchMode String @default("reject")
downstreamReceiptRetryEnabled Boolean @default(true)
downstreamUplinkRetryEnabled Boolean @default(true)
status String @default("active")
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[]
downstreamConnections CmppDownstreamConnection[]
connectionStates CmppConnectionState[]
gatewayDownstreamRecoveryStatuses GatewayDownstreamRecoveryStatus[]
gatewaySubmitDeadLetters GatewaySubmitDeadLetter[]
httpConfig SmsApplicationHttpConfig?
httpIpAllowlist SmsApplicationHttpIpAllowlist[]
httpApiCredentials HttpApiCredential[]
openApiRequests OpenApiRequest[]
httpWebhookEndpoints HttpWebhookEndpoint[]
httpWebhookEvents HttpWebhookEvent[]
dailyUsages SmsApplicationDailyUsage[]
inboundLongMessages CmppInboundLongMessage[]
@@index([tenantId, status])
}
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, 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])
@@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?
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[]
@@index([status])
}
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)
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])
}
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[]
}
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
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([signatureId, channelId])
@@index([signatureId, drainageItemId, channelId])
@@index([reportType, status])
}
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])
}
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)
createdAt DateTime @default(now())
completedAt DateTime?
@@index([tenantId, createdAt])
@@index([status, createdAt])
}
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?
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])
hits RiskHitRecord[]
@@unique([tenantId, code])
@@index([tenantId, 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 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
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?
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?
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[]
matchedUplinks SmsUplinkMessage[] @relation("SmsUplinkMatchedMessage")
uplinkMatchCandidates SmsUplinkMatchCandidate[]
downstreamDeliveries CmppDownstreamDelivery[]
@@index([tenantId, status, queuedAt])
@@index([batchTaskId, status])
@@index([reviewTaskId, status])
@@index([phoneNumber])
@@index([gatewayMessageId])
@@index([drainageInfoId, queuedAt])
@@unique([applicationId, clientMessageId])
}
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
sessionId String?
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])
session CmppSubmitSession? @relation(fields: [sessionId], references: [id])
segmentAudits SmsMessageSegmentAudit[]
@@index([tenantId, createdAt])
@@index([messageRecordId])
@@index([gatewayMessageId])
@@index([channelId, gatewayMessageId])
}
model DailyReconciliationReport {
id String @id @default(cuid())
reportDate DateTime @db.Date
tenantId String
tenantName String
applicationId String
applicationName String
sentUnits 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?
sentUnits 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?
sentUnits 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?
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])
@@index([tenantId, createdAt])
@@index([messageId])
@@index([gatewayMessageId])
@@index([channelId, gatewayMessageId])
@@index([channelId, gatewayMessageId, phoneNumber])
}
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([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?
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])
@@index([tenantId, status, createdAt])
@@index([applicationId, status, createdAt])
@@index([messageId])
@@index([messageRecordId])
@@index([deliveryType, status])
@@index([status, ackDeadlineAt])
}
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])
}