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 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") } 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? 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 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[] downstreamConnections CmppDownstreamConnection[] connectionStates CmppConnectionState[] gatewayDownstreamRecoveryStatuses GatewayDownstreamRecoveryStatus[] gatewaySubmitDeadLetters GatewaySubmitDeadLetter[] httpConfig SmsApplicationHttpConfig? httpIpAllowlist SmsApplicationHttpIpAllowlist[] httpApiCredentials HttpApiCredential[] openApiRequests OpenApiRequest[] httpWebhookEndpoints HttpWebhookEndpoint[] httpWebhookEvents HttpWebhookEvent[] dailyUsages SmsApplicationDailyUsage[] inboundLongMessages CmppInboundLongMessage[] riskRules RiskRule[] @@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? 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]) @@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 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, 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]) @@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[] @@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 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[] @@unique([applicationId, clientMessageId]) @@index([tenantId, status, queuedAt]) @@index([queuedAt]) @@index([batchTaskId, status]) @@index([reviewTaskId, status]) @@index([phoneNumber]) @@index([gatewayMessageId]) @@index([drainageInfoId, 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[] @@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? 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([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[] @@index([tenantId, status, createdAt]) @@index([applicationId, status, createdAt]) @@index([messageId]) @@index([messageRecordId]) @@index([deliveryType, status]) @@index([status, ackDeadlineAt]) } 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]) }