generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } enum RecurrenceType { hourly daily weekly monthly yearly } enum TargetType { todo reminder_task } enum ChannelType { inapp bark } enum NotificationStatus { pending queued sent failed } model User { id String @id @default(cuid()) username String @unique passwordHash String avatar String? timezone String @default("Asia/Shanghai") barkUrl String? inappEnabled Boolean @default(true) barkEnabled Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt invites Invite[] @relation("InviteCreator") todos Todo[] @relation("TodoOwner") createdTasks ReminderTask[] @relation("TaskCreator") taskRecipients ReminderTaskRecipient[] notifications Notification[] @relation("NotificationRecipient") } model Invite { id String @id @default(cuid()) code String @unique creatorId String maxUses Int @default(5) usedCount Int @default(0) expiresAt DateTime revokedAt DateTime? createdAt DateTime @default(now()) creator User @relation("InviteCreator", fields: [creatorId], references: [id]) @@index([creatorId]) @@index([expiresAt]) } model RecurrenceRule { id String @id @default(cuid()) type RecurrenceType interval Int @default(1) byWeekday Int? byMonthday Int? timezone String @default("Asia/Shanghai") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt todos Todo[] tasks ReminderTask[] } model Todo { id String @id @default(cuid()) ownerId String title String description String? dueAt DateTime recurrenceRuleId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt owner User @relation("TodoOwner", fields: [ownerId], references: [id]) recurrenceRule RecurrenceRule? @relation(fields: [recurrenceRuleId], references: [id]) @@index([ownerId, dueAt]) @@index([recurrenceRuleId]) } model ReminderTask { id String @id @default(cuid()) creatorId String title String description String? dueAt DateTime recurrenceRuleId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt creator User @relation("TaskCreator", fields: [creatorId], references: [id]) recurrenceRule RecurrenceRule? @relation(fields: [recurrenceRuleId], references: [id]) recipients ReminderTaskRecipient[] @@index([creatorId, dueAt]) @@index([recurrenceRuleId]) } model ReminderTaskRecipient { taskId String userId String task ReminderTask @relation(fields: [taskId], references: [id]) user User @relation(fields: [userId], references: [id]) @@id([taskId, userId]) @@index([userId]) } model ReminderOffset { id String @id @default(cuid()) targetType TargetType targetId String offsetMinutes Int channelInapp Boolean @default(true) channelBark Boolean @default(false) createdAt DateTime @default(now()) @@index([targetType, targetId]) } model Notification { id String @id @default(cuid()) recipientId String targetType TargetType targetId String triggerAt DateTime channel ChannelType status NotificationStatus @default(pending) lockedAt DateTime? sentAt DateTime? readAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt recipient User @relation("NotificationRecipient", fields: [recipientId], references: [id]) logs DeliveryLog[] @@unique([recipientId, targetType, targetId, triggerAt, channel]) @@index([status, triggerAt]) @@index([recipientId, readAt]) } model DeliveryLog { id String @id @default(cuid()) notificationId String attemptNo Int channel ChannelType status NotificationStatus responseMeta Json? createdAt DateTime @default(now()) notification Notification @relation(fields: [notificationId], references: [id]) @@index([notificationId]) }