generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model NotificationDelivery { id String @id @default(uuid()) projectCode String idempotencyKey String requestHash String recipientUserId String businessTitle String renderedTitle String content String actionTitle String actionUrl String status String @default("pending") attemptCount Int @default(0) maxAttempts Int @default(4) nextAttemptAt DateTime @default(now()) leaseOwner String? leaseExpiresAt DateTime? processQueryKey String? lastErrorCode String? lastErrorMessage String? isTest Boolean @default(false) sentAt DateTime? readAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([projectCode, idempotencyKey]) @@index([status, nextAttemptAt]) @@index([leaseExpiresAt]) @@index([processQueryKey]) } model RequestNonce { id String @id @default(uuid()) projectCode String nonce String expiresAt DateTime createdAt DateTime @default(now()) @@unique([projectCode, nonce]) @@index([expiresAt]) } model DingtalkLoginTicket { id String @id @default(uuid()) projectCode String audience String ticketHash String @unique ticketNonce String @unique authCodeHash String @unique dingtalkUserId String redirectPath String expiresAt DateTime consumedAt DateTime? createdAt DateTime @default(now()) @@index([projectCode, audience, expiresAt]) @@index([expiresAt, consumedAt]) } model NotificationProject { id String @id @default(uuid()) code String @unique name String secretCiphertext String secretNonce String secretAuthTag String allowedHostsJson String loginEnabled Boolean @default(false) loginCallbackUrl String? loginAllowedRedirectPrefixesJson String? isActive Boolean @default(true) importedFromEnvAt DateTime? lastSecretRotatedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt recipients NotificationProjectRecipient[] @@index([isActive, updatedAt]) } model NotificationProjectRecipient { id String @id @default(uuid()) projectId String aliasDisplay String aliasNormalized String userIdCiphertext String userIdNonce String userIdAuthTag String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt project NotificationProject @relation(fields: [projectId], references: [id], onDelete: Cascade) @@unique([projectId, aliasNormalized]) @@index([projectId, updatedAt]) } model NotificationAudit { id String @id @default(uuid()) actorType String actorId String action String targetProjectCode String? requestId String? ipAddress String? summary String detailsJson String? createdAt DateTime @default(now()) @@index([targetProjectCode, createdAt]) @@index([action, createdAt]) }